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."urn:schemas-microsoft-com:office:office" />

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

Comments

  • Anonymous
    November 23, 2003
    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. )
  • Anonymous
    November 23, 2003
    The comment has been removed
  • Anonymous
    November 23, 2003
    Brad, How did VB handle default (or optional parameters) in COM. Wouldn't we be faced with the same problem?
  • Anonymous
    November 23, 2003
    VB5 optionals were much more like a named and open-ended, but untyped parameter listpublic function f (a as integer, optional b as variant) as integer if ismissing(b) then b=2 f=a*bend 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.
  • Anonymous
    November 23, 2003
    Hi,you are writing always something about Design Guidelines. Is there any place, where to download up-to-date complete version of them?thanks
  • Anonymous
    November 24, 2003
    The comment has been removed
  • Anonymous
    November 24, 2003
    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?
  • Anonymous
    November 24, 2003
    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.
  • Anonymous
    November 25, 2003
    The comment has been removed
  • Anonymous
    November 26, 2003
    The comment has been removed
  • Anonymous
    December 03, 2003
    The comment has been removed
  • Anonymous
    June 01, 2004
    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).