method overloading vs optional parameter in C# 4.0

77,487

Solution 1

One good use case for 'Optional parameters' in conjunction with 'Named Parameters' in C# 4.0 is that it presents us with an elegant alternative to method overloading where you overload method based on the number of parameters.

For example say you want a method foo to be be called/used like so, foo(), foo(1), foo(1,2), foo(1,2, "hello"). With method overloading you would implement the solution like this,

///Base foo method
public void DoFoo(int a, long b, string c)
{
   //Do something
}  

/// Foo with 2 params only
public void DoFoo(int a, long b)
{
    /// ....
    DoFoo(a, b, "Hello");
}

public void DoFoo(int a)
{
    ///....
    DoFoo(a, 23, "Hello");
}

.....

With optional parameters in C# 4.0 you would implement the use case like the following,

public void DoFoo(int a = 10, long b = 23, string c = "Hello")

Then you could use the method like so - Note the use of named parameter -

DoFoo(c:"Hello There, John Doe")

This call takes parameter a value as 10 and parameter b as 23. Another variant of this call - notice you don't need to set the parameter values in the order as they appear in the method signature, the named parameter makes the value explicit.

DoFoo(c:"hello again", a:100) 

Another benefit of using named parameter is that it greatly enhances readability and thus code maintenance of optional parameter methods.

Note how one method pretty much makes redundant having to define 3 or more methods in method overloading. This I have found is a good use case for using optional parameter in conjunction with named parameters.

Solution 2

Optional Parameters provide issues when you expose them publicly as API. A rename of a parameter can lead to issues. Changing the default value leads to issues (See e.g. here for some info: Caveats of C# 4.0 optional parameters)

Also, optional params can only be used for compile-time constants. Compare this:

public static void Foo(IEnumerable<string> items = new List<string>()) {}
// Default parameter value for 'items' must be a compile-time constant

to this

public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good

Update

Here's some additional reading material when a constructor with default parameters does not play nicely with Reflection.

Solution 3

I believe they serve different purposes. Optional parameters are for when you can use a default value for a parameter, and the underlying code will be the same:

public CreditScore CheckCredit( 
  bool useHistoricalData = false,  
  bool useStrongHeuristics = true) { 
  // ... 
}

Method overloads are for when you have mutually-exclusive (subsets of) parameters. That normally means that you need to preprocess some parameters, or that you have different code altogether for the different "versions" of your method (note that even in this case, some parameters can be shared, that's why I mentioned "subsets" above):

public void SendSurvey(IList<Customer> customers, int surveyKey) {  
  // will loop and call the other one 
} 
public void SendSurvey(Customer customer, int surveyKey) {  
  ...  
}

(I wrote about this some time ago here)

Solution 4

This one almost goes without saying, but:

Not all languages support optional parameters. If you want your libraries to be friendly to those languages, you have to use overloads.

Granted, this isn't even an issue for most shops. But you can bet it's why Microsoft doesn't use optional parameters in the Base Class Library.

Solution 5

Neither is definitively "better" than the other. They both have their place in writing good code. Optional parameters should be used if the parameters can have a default value. Method overloading should be used when the difference in signature goes beyond not defining parameters that could have default values (such as that the behavior differs depending on which parameters are passed, and which are left to the default).

// this is a good candidate for optional parameters
public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0)

// this is not, because it should be one or the other, but not both
public void DoSomething(Stream streamData = null, string stringData = null)

// these are good candidates for overloading
public void DoSomething(Stream data)
public void DoSomething(string data)

// these are no longer good candidates for overloading
public void DoSomething(int firstThing)
{
    DoSomething(firstThing, 12);
}
public void DoSomething(int firstThing, int nextThing)
{
    DoSomething(firstThing, nextThing, 0);
}
public void DoSomething(int firstThing, int nextThing, int lastThing)
{
    ...
}
Share:
77,487
Louis Rhys
Author by

Louis Rhys

trying to learn and help others learn :)

Updated on March 23, 2020

Comments

  • Louis Rhys
    Louis Rhys about 4 years

    which one is better? at a glance optional parameter seems better (less code, less XML documentation, etc), but why do most MSDN library classes use overloading instead of optional parameters?

    Is there any special thing you have to take note when you choose to use optional parameter (or overloading)?

  • Louis Rhys
    Louis Rhys almost 14 years
    is this an important consideration?
  • Martin Ingvar Kofoed Jensen
    Martin Ingvar Kofoed Jensen almost 14 years
    Less code is always better. Less risk of errors and more readable.
  • Bikal Lem
    Bikal Lem almost 14 years
    you can use named parameter in conjunction with optional parameter to avoid having to define the optional parameter around the end of the code. See my post below.
  • Martin Ingvar Kofoed Jensen
    Martin Ingvar Kofoed Jensen almost 14 years
    Yes, I agree. I have also added that to my post
  • Louis Rhys
    Louis Rhys almost 14 years
    what happens if you use a library with an optional parameter from a language that does not support it (ex. C# 2.0)?
  • Joe White
    Joe White almost 14 years
    C# 2.0 ignores the "optional parameter" flag, and treats all parameters as required. So every call site has to provide a value for every parameter.
  • Anlo
    Anlo about 12 years
    You still need to check for nulls when using optional parameters, please see my answer here.
  • Thomas S. Trias
    Thomas S. Trias about 12 years
    The fact that the CALLING site has to have the defaults injected is definitely an issue for a public API. If only we could get an optional parameter variant that is actually syntactic sugar for creating the overloads (while still keeping the existing one around for things like COM Interop).
  • Michael Parker
    Michael Parker almost 11 years
    Someone downvoted you, presumably for not answering the question. But I think this is a useful observation nonetheless so +1. Grouping parameters into objects is a refactoring that isn't performed often enough.
  • Chris Pratt
    Chris Pratt about 10 years
    For my money, this is the best answer. You're the only one to make the distinction of what the method body does. It makes perfect sense that if the method body is the same, then use optional params, but if the method body will vary based on the params, then use overloads.
  • mrmillsy
    mrmillsy about 10 years
    Regarding renaming the parameters, this is already an issue regardless of the use of optional parameters or overloaded methods. The consumer can specify a named parameter and renaming the variable will break the compile.
  • Vishal Anand
    Vishal Anand about 7 years
    Guard clauses will increase in case of optional parameters if decision making is required.
  • Crob
    Crob about 6 years
    Link broken again...use the non-beta Wayback Machine link and it works. web.archive.org/web/20140815160502/http://blog.coverity.com/‌​…
  • Shishir Gupta
    Shishir Gupta over 4 years
    All those who are going to use this approach - FYI you cannot change the name of parameters going forward if you use named parameters in caller, unless changing it in each caller (which is simple refactoring if all the reference are in same solution, but not if you use it as a library in other project). Having said that, changing parameter name is always considered a breaking change anyhow (even if not using named parameters!)
  • Saurabh Rana
    Saurabh Rana almost 4 years
    perfect explanation. Thanks.