24

I recently learned that you can create some method with unlimited parameters, for example:

SomeMethod(params int[] numbers);

but my question is, what's the difference between that and just creating a method that receives a list or an array?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

perhaps it has some impact in performance? I don't fully understand or see in what way you would prefer the one with unlimited parameters.

A quick search on google didn't help, I hope you could help me.

  • look at this : https://stackoverflow.com/questions/434761/array-versus-listt-when-to-use-which – A Farmanbar Jan 22 '20 at 17:18
  • In addition to my answer below, `params` also _requires_ the type of the argument to be an array. If you need to consume a collection other than an array, it may make more sense to provide a non-`params` argument instead. – digitlworld Jan 22 '20 at 17:36
  • 1
    @digitlworld: If this subject interests you, see https://github.com/dotnet/csharplang/issues/179 for discussion. – Eric Lippert Jan 22 '20 at 18:26
  • Look at the signature and/or implementation of `Console.Write`. – 3Dave Jan 22 '20 at 18:36

5 Answers5

35

what's the difference between that and just creating a method that receives a list or an array?

The difference between

void M(params int[] x)

and

void N(int[] x)

is that M may be called like this:

M(1, 2, 3)

or like this:

M(new int[] { 1, 2, 3 });

but N may only be called in the second way, not the first way.

perhaps it has some impact in performance?

The impact to performance is that whether you call M in the first way or the second way, either way you get an array created. Creating an array has a performance impact because it takes both time and memory. Remember that performance impacts should be measured against performance goals; it is unlikely that the cost of creating an extra array is the gating factor that is the difference between success and failure in the marketplace.

I don't fully understand or see in what way you would prefer the one with unlimited parameters.

It is purely and entirely a convenience for the author of the code which is calling the method; it is simply shorter and easier to write

M(1, 2, 3);

instead of writing

M(new int[] { 1, 2, 3 });

It just saves a few keystrokes on the caller's side. That is all.

A few questions you did not ask but perhaps would like to know the answer to:

What is this feature called?

Methods that allow a variable number of arguments to be passed on the caller side are called variadic. Params methods are how C# implements variadic methods.

How does overload resolution work with a variadic method?

When faced with an overload resolution problem, C# will consider both the "normal" and "expanded" forms, and the "normal" form always wins if both are applicable. For example, consider this:

void P(params object[] x){}

and we have a call

P(null);

There are two applicable possibilities. In "normal" form, we call P and pass a null reference for the array. In "expanded" form, we call P(new object[] { null }). In this case, normal form wins. If we had a call P(null, null) then normal form is inapplicable and expanded form wins by default.

Challenge: Suppose we have var s = new[] { "hello" }; and a call P(s);. Describe what happens at the call site and why. You might be surprised!

Challenge: Suppose we have both void P(object x){} and void P(params object[] x){}. What does P(null) do, and why?

Challenge: Suppose we have both void M(string x){} and void M(params string[] x){}. What does M(null) do, and why? How does this differ from the previous case?

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Ch2: `P(null)` vs. `P((object)null)` vs. `P((object[])null)` - where can I find explanation for this difference? *It feels* like `null` had some *special type*, which converts to array rather than object (`P(null)`), but find conversion to string or array of strings ambiguous (`M(null)`)... which feels very strange, would expect it to either be ambiguous in both cases or pick the single-arg version (which it does not). But I guess it is more about `params` being somehow *generic*, like universal reference (`&&`) in C++ templates, and thus better match (in Ch2, not in Ch3). – firda Jan 28 '20 at 12:04
  • 1
    @firda: You can find the explanation in the C# specification. The reason for the strangeness is: null is convertible to object, string, object[] and string[]. So the question then posed to overload resolution is: which conversion is *best*? The controlling rule in this case is *specific is better than general*. How do we tell which type is the more specific? The rule is: all giraffes are animals but not all animals are giraffes, therefore, giraffe is more specific than animal. All `object[]` are `object`, but not all `object` are `object[]`, therefore `object[]` is more specific. – Eric Lippert Jan 28 '20 at 16:12
  • 1
    @firda: Now you know why strings are ambiguous. It is NOT true that "all `string[]` are `string`." In fact NO `string[]` are `string` and NO `string` are `string[]`, so `string` is not more specific or more general than `string[]`, and we get an ambiguity error when asked to choose. – Eric Lippert Jan 28 '20 at 16:13
  • *All `object[]` are `object`...`object[]` is more specific* so `P(null)` goes for the more specific array, thx, that is what I was missing. Found some rules here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#better-function-member – firda Jan 28 '20 at 17:47
5

Just did a little prototype. The answer appears to be that params is simply syntactic sugar for passing in an array. That's not really a surprise. I created two versions of the same method, where the only difference is the "params" keyword. The IL generated for both was identical, except that a System.ParamArrayAttribute was applied to the params version.

Further, the IL generated at the call site was also the same between me calling the method with a manually declared new int[] and calling the method just using the params arguments.

So, the answer seems to be "convenience". There doesn't appear to be any difference in performance. You can also call a params function with an array instead, so that's also not terribly surprising. It comes down to if it's easier for the consumer of your method to call it with any number of parameters (e.g. someMethod(1, 2, 3)) than to always have to create a collection first (e.g. someMethod(new List<int>() { 1, 2, 3 } )).

digitlworld
  • 1,046
  • 7
  • 13
4

The feature of unlimited parameters offers the following benefits in many scenarios:

  1. Loose coupling
  2. Enhanced reusability
  3. Better overall performance of the application

Here is an example where the unlimited parameters option is a great choice

Consider that an application for sending emails needs to be built.

The function that sends the email must be able to handle either single or multiple values for the 'To', 'CC' and 'BCC' fields.

If the types of parameters are fixed to be arrays or lists for all fields (To, CC, BCC), then the calling function will be forced to deal with all the complexity of defining 3 arrays or lists in order to call the email sender function.

Even if the caller wants to send an email to just one address, the email sender function will force the caller to define and send 3 different arrays as parameters.

If the email sender function takes the unlimited params approach, then the caller function need not deal with all the complexity.

The unlimited parameters approach contributes to better runtime perfomance of the application by avoiding the creation of arrays or lists wherever unnecessary.

Gopinath
  • 4,066
  • 1
  • 14
  • 16
2

From a non-performance, style perspective, the params keyword is really nice to have when you want to send optional list of parameters.

Personally, I would use params when my code was something like

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

The nice part about this is that I can use this method all over the place without having to create an array or list every time.

I would use array or list when I know I will always be passing this function a set of data that is already put together like

List<User> users = GetUsersFromDB();
SomeMethod(users);

I see the benefit of params in the flexibility that it adds. It may be a relatively minor impact to your code, but it is still a nice tool to have.

OliveJuice
  • 368
  • 1
  • 7
1

The calling convention is different. For example ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

In the first case, you can pass as many ints as separate parameters to the method. In the second, you would need to instantiate a list and pass it.

JP Alioto
  • 44,864
  • 6
  • 88
  • 112