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