Design Guidelines Update: Variable Number of Arguments

Based on some very good feedback from Eric
Gunnerson
, I recently updated the Design Guidelines with
some more info on Params.

As always, comments welcome.

Have a guideline that is consistent with the current set that you’d like
to see added? Write it up in this
format and I will see what I can do.

Variable Number of Arguments[1]

Methods that can take an unlimited number of
parameters are expressed by providing an overload that takes an array. For
example, String.Format could provides the following
overload:

string
String.Format(string format, object[] args)

A user can then
write:

    String.Format("{0} {1} {2} {3}", new object[] {1, 2,
3, 4});

Adding the
params keyword to the last parameter:

    string String.Format(string format, params object[]
args)

provides a
shortcut to creating the temporary array, and allows the user to
write:

    String.Format("{0} {1} {2} {3}", 1, 2, 3,
4);

Users expect to
use the short form, and it's therefore important to use "params" where
appropriate.

           
Don't use params if the user
would not expect to use the short form. Consider:

public
void Write(byte[] value);

In this case, the user would not expect to be able to specify
individual bytes, so params would not be
appropriate.

*
Do use params if all overloads
are performing the identical operation. For example:

public
void Combine(Delegate delegate1, Delegate
delegate2);
public void Combine(Delegate[]
delegates);

Adding
params to the second overload allows the use the same idiom in their code
regardless of the number of arguments.

*
Consider using Params if a simple
overload could use params but a more complex one could not, ask yourself "would
users value the utility of having params on one overload even if it wasn't on
all overloads". Consider the following overloaded
methods:
       
ExecuteAssembly(String,Evidence,String[])

ExecuteAssembly(String,Evidence,String[],Byte[],AssemblyHashAlgorithm)

Even if the parameters could be re-ordered in the second
overload to place an array as the last parameter, because there are two arrays,
it's not a good candidate to use params. But it could be used on the first
overload. If the first overload is often used, users will appreciate the
simplification of using params. The correct params usage in this case
is:
public int ExecuteAssembly(string assemblyFile, Evidence
assemblySecurity, params string[]
args);
public int
ExecuteAssembly(string assemblyFile, Evidence assemblySecurity, string[] args, byte[] hashValue, AssemblyHashAlgorithm
hashAlgorithm);

A
simpler example is the following:
       
FillPolygon(Brush,PointF[])

FillPolygon(Brush,PointF[],FillMode)
The user will want "params" on the first version, event though they
can't use it on the second version. The correct params usage in this case
is:

       public
void FillPolygon(Brush brush, params
PointF[] points)
public void
FillPolygon(Brush brush,PointF[] points,FillMode fillmode)

*
Do order parameters so that it's
possible to apply "params" to the methods. Consider the
following:

       
Find(IComparer)

Find(String[],IComparer)

Find(String[])

Because of
the ordering of parameters on the second overload, the opportunity to use
"params" has been lost. If this had been written as:

       
Find(IComparer)
Find(IComparer,
String[])

Find(String[])

"params" could have been used for both
overloads.

*
Do use the
params construct instead of several overloaded methods for repeated
arguments[2].

*
Do add the params
keyword to a parameter that meets these guidelines even if you have already
shipped it once undecorated. Adding
params is NOT a breaking
change. Existing client code will
continue to work as expected and new code can start to take advantage of params.

           
Do not use the params when the array can is modified by the method.
Because the array is a temporary any modifications to the array will be
lost.

           
Do not use the VarArgs calling convention, otherwise known
as the ellipsis (…), exclusively because the Common Language Specification does
not support it[3].

For extremely performance sensitive code, you might want to provide
special code paths for a small number of elements. You should only do this if you are going
to special case the entire code path (not just create an array and call the more
general method). In such cases, we
recommend the following pattern as a balance between performance and the cost of
specially cased code.

void Format (string formatString, object
arg1)
void Format (string formatString, object arg1, object
arg2)


void Format (string formatString, params
object [] args)

[1] Substantial contribution from
Eric
Gunnerson

[2] Fully covered by FxCop rule:
DesignRules/ConsiderReplacingRepetitiveArgsWithParameterArray

[3] Coming soon : FxCopBug