Should you declare methods using overloads or optional parameters in C# 4.0?

22,496

Solution 1

I'd consider the following:

  • Do you need your code to be used from languages which don't support optional parameters? If so, consider including the overloads.
  • Do you have any members on your team who violently oppose optional parameters? (Sometimes it's easier to live with a decision you don't like than to argue the case.)
  • Are you confident that your defaults won't change between builds of your code, or if they might, will your callers be okay with that?

I haven't checked how the defaults are going to work, but I'd assume that the default values will be baked into the calling code, much the same as references to const fields. That's usually okay - changes to a default value are pretty significant anyway - but those are the things to consider.

Solution 2

When a method overload normally performs the same thing with a different number of arguments then defaults will be used.

When a method overload performs a function differently based on its parameters then overloading will continue to be used.

I used optional back in my VB6 days and have since missed it, it will reduce a lot of XML comment duplication in C#.

Solution 3

I've been using Delphi with optional parameters forever. I've switched to using overloads instead.

Because when you go to create more overloads, you'll invariably conflict with an optional parameter form, and then you'll have to convert them to non-optional anyway.

And I like the notion that there's generally one super method, and the rest are simpler wrappers around that one.

Solution 4

I will definitely be using the optional parameters feature of 4.0. It gets rid of the ridiculous ...

public void M1( string foo, string bar )
{
   // do that thang
}

public void M1( string foo )
{
  M1( foo, "bar default" ); // I have always hated this line of code specifically
}

... and puts the values right where the caller can see them ...

public void M1( string foo, string bar = "bar default" )
{
   // do that thang
}

Much more simple and much less error prone. I've actually seen this as a bug in the overload case ...

public void M1( string foo )
{
   M2( foo, "bar default" );  // oops!  I meant M1!
}

I have not played with the 4.0 complier yet, but I would not be shocked to learn that the complier simply emits the overloads for you.

Solution 5

Optional parameters are essentially a piece of metadata which directs a compiler that's processing a method call to insert appropriate defaults at the call site. By contrast, overloads provide a means by which a compiler can select one of a number of methods, some of which might supply default values themselves. Note that if one tries to call a method that specifies optional parameters from code written in a language which doesn't support them, the compiler will require that the "optional" parameters be specified, but since calling a method without specifying an optional parameter is equivalent to calling it with a parameter equal to the default value, there's no obstacle to such languages calling such methods.

A significant consequence of binding of optional parameters at the call site is that they will be assigned values based upon the version of the target code which is available to the compiler. If an assembly Foo has a method Boo(int) with a default value of 5, and assembly Bar contains a call to Foo.Boo(), the compiler will process that as a Foo.Boo(5). If the default value is changed to 6 and assembly Foo is recompiled, Bar will continue to call Foo.Boo(5) unless or until it is recompiled with that new version of Foo. One should thus avoid using optional parameters for things that might change.

Share:
22,496
Meydjer Luzzoli
Author by

Meydjer Luzzoli

Updated on July 08, 2022

Comments

  • Meydjer Luzzoli
    Meydjer Luzzoli almost 2 years

    I was watching Anders' talk about C# 4.0 and sneak preview of C# 5.0, and it got me thinking about when optional parameters are available in C# what is going to be the recommended way to declare methods that do not need all parameters specified?

    For example something like the FileStream class has about fifteen different constructors which can be divided into logical 'families' e.g. the ones below from a string, the ones from an IntPtr and the ones from a SafeFileHandle.

    FileStream(string,FileMode);
    FileStream(string,FileMode,FileAccess);
    FileStream(string,FileMode,FileAccess,FileShare);
    FileStream(string,FileMode,FileAccess,FileShare,int);
    FileStream(string,FileMode,FileAccess,FileShare,int,bool);
    

    It seems to me that this type of pattern could be simplified by having three constructors instead, and using optional parameters for the ones that can be defaulted, which would make the different families of constructors more distinct [note: I know this change will not be made in the BCL, I'm talking hypothetically for this type of situation].

    What do you think? From C# 4.0 will it make more sense to make closely related groups of constructors and methods a single method with optional parameters, or is there a good reason to stick with the traditional many-overload mechanism?

  • Ilya Ryzhenkov
    Ilya Ryzhenkov over 15 years
    I've heard optional parameters should be last, shouldn't they?
  • Robert P
    Robert P over 15 years
    Depends on your design. Perhaps the 'start' argument is normally important, except when it's not. Perhaps you have the same signature somewhere else, meaning something different. For a contrived example, public Rectangle(int width, int height, Point innerSquareStart, Point innerSquareEnd) {}
  • legends2k
    legends2k almost 10 years
    +1 for the wisdom on pragmatism: Sometimes it's easier to live with a decision you don't like than to argue the case.
  • Jon Skeet
    Jon Skeet over 9 years
    @romkyns: No, the effect of overloads isn't the same thing with point 3. With overloads providing the defaults, the defaults are within the library code - so if you change the default and provide a new version of the library, callers will see the new default without recompilation. Whereas with optional parameters, you'd need to recompile to "see" the new defaults. A lot of the time it's not an important distinction, but it is a distinction.
  • SHEKHAR SHETE
    SHEKHAR SHETE over 7 years
    hi @JonSkeet, i would like to know if we use both i.e function with optional paramater and other with overloading which method will be called?? eg Add(int a, int b) and Add(int a,int b,int c=0) and function call say: Add(5,10); which method will be called overloaded function or optional parameter function ? thanks :)
  • Jon Skeet
    Jon Skeet over 7 years
    @Shekshar: Have you tried it? Read the spec for details, but basically in a tie-breaker a method where the compiler hasn't had to fill in any optional parameters wins.
  • SHEKHAR SHETE
    SHEKHAR SHETE over 7 years
    @JonSkeet just now i tried with above...the function overloading wins over optional parameter :)
  • stakx - no longer contributing
    stakx - no longer contributing about 7 years
    Re: "One should thus avoid using optional parameters for things that might change." I agree that this can be problematic if the change goes unnoticed by client code. However, the same problem exists when the default value is hidden inside a method overload: void Foo(int value) … void Foo() { Foo(42); }. From the outside, the caller does not know what default value gets used, nor when it might change; one would have to monitor written documentation for that. Default values for optional parameters can be seen as just that: documentation-in-code what the default value is.
  • supercat
    supercat about 7 years
    @stakx: If a parameterless overload chains to an overload with a parameter, changing the "default" value of that parameter and recompiling the definition of the overload will change the value it uses even if the calling code is not recompiled.
  • stakx - no longer contributing
    stakx - no longer contributing about 7 years
    True but that doesn't make it more problematic than the alternative. In one case (method overload), calling code has no say in the default value. This can be appropriate if the calling code really doesn't care at all about the optional parameter and what it means. In the other case (optional parameter with default value), previously compiled calling code won't be affected when the default value changes. This can also be appropriate when the calling code actually cares about the parameter; omitting it in source is like saying, "the currently suggested default value is OK for me."
  • stakx - no longer contributing
    stakx - no longer contributing about 7 years
    The point I'm trying to make here is that while there are consequences to either approach (like you pointed out), they are not inherently advantageous or disadvantageous. That depends also on the calling code's needs and goals. From that POV, I found the verdict in your answer's last sentence somewhat too rigid.
  • supercat
    supercat about 7 years
    @stakx: I said "avoid using" rather than "never use". If changing X will mean that the next recompilation of Y will change Y's behavior, that will either require that a build system be configured so that every recompilation of X also recompiles Y (slowing things down), or create the risk that a programmer will change X in a way that will break Y the next time it's compiled, and only discover such breakage at a later time when Y is changed for some totally unrelated reason. Default parameters should only be used when their advantages outweigh such costs.
  • Dan Lugg
    Dan Lugg about 7 years
    I very much agree with this, however there is the caveat that when you have a method that takes multiple (3+) parameters, that are by nature all "optional" (could be substituted with a default) you could end up with many permutations of the method signature for no more benefit. Consider Foo(A, B, C) requires Foo(A), Foo(B), Foo(C), Foo(A, B), Foo(A, C), Foo(B, C).
  • CodingYoshi
    CodingYoshi about 2 years
    "Now false will have an unintended effect, the event will no longer be treated as critical."--I am not sure if this is correct. It would never have been treated as critical before the refactor also because in HandleError("Disk is full", false); the false is for silent.