Default arguments and versioning

I was recently asked about why the CLS (and the Design Guidelines) discourages default arguments.  So I added this annotation to the Design Guidelines document.  I believe it is the same reason why C# (rightfully) left it out of the language.

 

 

The issue with default arguments and versioning is that once you define a default argument you can never change its value.  Consider the following method (in VB as C# does not support default arguments; the basic issue exists in C++ as well).

   Public Shared Function ComputeTotal(ByVal subtotal As Double,

                                     Optional ByVal salesTaxPrecent As Double = 8.8) As Double

        ComputeTotal = subtotal + subtotal * salesTaxPrecent / 100

    End Function

 

Call site:

Foo.ComputeTotal(100)

 

Looking at the IL disassembly for this call is instructive in showing what is going on:
   IL_0001:  ldc.r8     100.

  IL_000a:  ldc.r8     8.8000000000000007

  IL_0013:  call       float64 ConsoleApplication5.Foo::ComputeTotal(float64,

                                                                     float64)

 

Notice in line 000a the VB compiler generated the IL instruction to load the literal 8.8.  This is burned into the call site.  If we change the definitions of the default value for salesTaxPrecent to 7.8% and do not recompile the calling code (as is common when working with large frameworks) 8.8 will continue to be passed as the default value.  This can be a very nasty bug to track down!

Using method overloading encapsulates the default value so that it can be changed in a predictable way.

 

   Public Shared Function ComputeTotal(ByVal subtotal As Double, ByVal salesTaxPrecent As Double)

                                       As Double

        ComputeTotal = subtotal + subtotal * salesTaxPrecent / 100

    End Function

 

    Public Shared Function ComputeTotal(ByVal subtotal As Double) As Double

        ComputeTotal = ComputeTotal(subtotal, 8.8)

    End Function

 

 

Published 23 November 03 10:31 by BradA

Comments

# Mark Hurd said on November 23, 2003 11:58 PM:
So don't do that. I'm sure there'll be Attribute changes that cause the same problem. Which is preferable: Highlight to API designers that they shouldn't use "magic" defaults the might change, or require all APIs to document what the default values are for otherwise unmentioned 'arguments' to overloaded methods? And they're good for scripting languages too. (Note that overload methods *do* have their uses, but providing default argument values is a minor one -- nevertheless it *is* the valid approach in the scenario described in this post. )
# Dan Sullivan said on November 24, 2003 12:18 AM:
But C# hasn't left the effect of this out of the language. For example, public class foo { public const int goo = 3; } will produce the same problem if the value of goo is changed and an update is done without rebuilding the users of the assembly. Note that the current guidelines recommend using const when you can. If the VB guideline were applied to C# you would have to discouge the use of const fields and replace them with static readonly properties. Maybe the current guideline should add a recommendation that const field be considered immutable, much like COM interfaces. Likewise a recommendation could be added that default values should be considered immutable too. It seems like telling people how to use something that is in the language safely would be better than telling them not to use it at all. I'm sure that there is lots of VB code being ported that has default values, so it is not like they are going to go away.
# rizzo said on November 24, 2003 1:29 AM:
Brad, How did VB handle default (or optional parameters) in COM. Wouldn't we be faced with the same problem?
# ries said on November 24, 2003 3:00 AM:
VB5 optionals were much more like a named and open-ended, but untyped parameter list public function f (a as integer, optional b as variant) as integer if ismissing(b) then b=2 f=a*b end function Also, on caller side, VB allows specifying naming the parameters in the method call (while C# only allows this for Attributes). Can anyone give a reason why C# developers are not allowed to write richer code, like ComputeTotal = ComputeTotal(subtotal, salesTaxPercent:=8.8) ? I really miss being able to do this.
# Jakub Skopal said on November 24, 2003 6:11 AM:
Hi, you are writing always something about Design Guidelines. Is there any place, where to download up-to-date complete version of them? thanks
# Tor said on November 24, 2003 9:56 AM:
Seems to me that the real issue with the example you give is that default arguments is not supported in IL, so the VB compiler has to cheat by actually figuring out what the default argument is and supplying that. So the question isn't so much whether C# should support it, but rather whether IL should. If it did, there would be no reason why C# shouldn't support it too (at least not THAT reason).
# Luke Hutteman said on November 24, 2003 10:33 AM:
Not sure if I buy this as a valid reason not to include default parameters. * First of all, when you change a method signature it only makes sense to recompile all classes that call this method - I'm sure a param-type change from say int to double would cause problems as well. * Second of all, like Dan said, you still have this problem if you add public constants to your class. I would assume enum's also would pose a problem when changed * And last but not least, instead of embedding the default value on the calling end, why not implement this in the compiler by simply generating extra overloaded functions that call into the main function will all default values filled in?
# Keith Patrick said on November 24, 2003 2:23 PM:
I used to use static readonly instead of const, but I ran into a problem where I couldn't use my readonly values as attribute arguments, and since I do not use magic numbers at all, I had to revert to using consts. A bit of a pain, since const ended up causing problems elsewhere, so it came down to lesser of two evils, which is a choice I hate to make.
# dannyR said on November 26, 2003 3:02 AM:
Re: Dan Sullivan's comment. Yes, your example shows the same issue exists. However, it goes away if the code looks like this: public class foo { private const int goo = 3; public int Goo { get { return this.goo; }} } It doesn't completely negate your point, except that const fields don't have to have the problem you described if used this way.
# Shane King said on November 26, 2003 9:26 PM:
Couldn't the compiler see something like this (using C style syntax here): void foo(int i = 0) { } And produce code as if this had been written: void foo() { foo(0); } void foo(int i) { } That way, clients would automatically be updated, and you can keep your default values for arguments. Seems like a best of both worlds solution to me. One issue is that it might clash with an existing overload, but you've got that problem either way, so I don't see how this changes it.
# Jazper said on December 3, 2003 8:45 AM:
just the fact that people had to recomile it to take effect is a shitty reason for that problem. Why not just say, that C# does not support default parameters. its just because Microsoft decided to be so. it had not been impossible!
# C# 2.0 -- WHY DEFAULT PARAMETER VALUES? #### said on June 1, 2004 1:50 PM:
While the C# designer(s) may have had good intentions by not including
default parameters in the C# language, it is better "style" to inform
developers of the impact of one's decision instead of removing the
choice entirely. If "goto" can make it into the language, surely something
as useful as default parameter values could make it as well. (Even attributes
have, in effect, default parameters ... not to mention optional and named parameters.)

The issue that Microsoft has raised is important: default parameter values
technically should be part of the interface. The significance of that fact does
not imply that default parameters are not good design. To the contrary, the
clunky approaches that developers (and several C# publications) have advised as
work-arounds are a far worse outcome than if default parameters were used.

While the work-around suggested by Microsoft does not cause end-users of
such classes to re-compile, it instead pretends that the "default value"
chosen is un-important to the end-user (by removing it from the interface).
If an end-user of this class were to assume a certain default value and it
was later changed by the developer, then surely the end-user *should* be
notified. Besides increasing the code burden for the designer of a class,
this method can clutter the interface with a huge set of needless signatures.

A second approach commonly suggested is the use of a params object array.
This technique is used frequently in un-typed languages such as JavaScript.
However, this design choice removes the ability to cleanly type one's
parameters and also prevents the interface from advertising the default
values chosen by the class implementation.

The lack of default parameters, a relatively modern language enhancement, is
just silly. The removal of this feature (from the languages that C# is
based) is indeed a bad design choice. It is rather strange that a language
that gives developers the choice to use the "goto" statement does not give
developers the choice to publish and use default parameter values with their
class interface.

Eric Gunnerson has stated that customer feedback is the only way to make this change happen. So, make a difference by making your opinion heard on this. Go to http://www.gotdotnet.com/Community/MessageBoard/Thread.aspx?id=228307 and post your throughts. There are technical reasons on both sides of the argument, but it really boils down to style and utility. The MS language designers do seem to listen to the feedback (if you scream it in the right place).
New Comments to this post are disabled

Search

Go

This Blog

Syndication

Page view tracker