There are a number of examples in the .NET framework where there are multiple overloads for a method, some of which use a specific number of parameters followed by a final "catch all" where the params
keyword is used. Common examples of this are on the String
class e.g.:
I was wondering if there is a particular reason for why there are so many of these method overloads? At first I thought it might be something to do with performance; the question and answers to this SO question - Cost of using params in C# - would suggest so.
However, I started to delve into the .NET source code using the Reference Source website. I noticed this in the String class source code:
String.Concat()
actually runs different code depending on how many fixed arguments are used - this in my mind would definitely be an optimization. String.Format()
however just seems to provide wrappers around the main param
method - see below for the paraphrased code:
public static String Format(String format, Object arg0)
{
return Format(format, new Object[] { arg0 });
}
public static String Format(String format, Object arg0, Object arg1)
{
return Format(format, new Object[] { arg0, arg1 });
}
public static String Format(String format, Object arg0, Object arg1, Object arg2)
{
return Format(format, new Object[] { arg0, arg1, arg2 });
}
public static String Format(String format, params Object[] args)
{
// Do work here...
}
So are there performance benefits or can this simply be a matter of convenience, or maybe both? In the particular case above I do not see any obvious benefit and it just seems to duplicate work.
It allows you to create delegates of each of those signatures that invoke that method:
This wouldn't work with a
params
method; you could only assign that overload toFunc<string, object[]>
or a delegate that specifically providedparams
in its signature. You'd be forced to create a new method that just called the params overload to create a delegate of the signature used int he example (possibly through the use of a lambda).Update: Its not String.Concat but String.Format.
I guess the reason behind this is that all calls end up in StringBuilder.AppendFormat which is quite complex and it would be a major source of code duplication if you would handle every number of input arguments in a different way.
Things are different if you have an heavy duty API (like tracing) where the overhead of calling a method via a params overload is very significant. It can be up to a factor of 5. The same goes for implicit delegate allocations which is common with LINQ. These temp delegate instances are not cheap in optimized code. Roslyn for example has banned LINQ usage because of the high implicit delegate allocation costs.