Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why C# allows that only the last parameter of a method is of "variable length"

From what I know, C# allows that only the last parameter of a method is of "variable length", for example:

T f(A a, params B[] b) allows that if you have A r; .... B x, y, z; .... you can call f like f (r, x, y, z). Why C# doesn't also define something like:

T f(params A[] a, params B[] b)
like image 450
Tom Sarduy Avatar asked Jun 15 '11 01:06

Tom Sarduy


5 Answers

Because how would the compiler know when the variable arguments for the first parameter stop?

Pleas tell me what argOne and argTwo should contain inside of the method body:

void Foo( params object[] argOne, params object[] argTwo )
{
    // whatever
} 

Foo( 1, false, "Hello", new object(), 2.3 );
like image 65
Ed S. Avatar answered Oct 05 '22 04:10

Ed S.


Because it would be too complicated to determine when such a construction is actually allowed.
(When the call would be unambiguous)
Although it would be possible to create a good set of rules, they would be rather complicated and difficult to understand. People would end up asking why case X doesn't work, if it has a subtle ambiguity.

For example:

  • Neither type can be an interface or generic parameter
  • If one type is an enum or a numeric type, the other must be a class other than object or Enum
  • If one type is a delegate, the other must not also be a delegate type (nor object, Delegate, nor MulticastDelegate)
  • One type cannot inherit the other
  • All of these rules apply to any types implicitly convertible to the parameter types
  • Both types must be sealed or must be value types

(some of these rules could be enforced at the callsite instead)

In practice, such a feature would have so many restrictions as to be almost worthless.

Therefore, this feature would start with -10,000 points.

It would also create a whole new category of breaking changes. Unsealing a type, adding implicit conversions, or other seemingly trivial things could now break client code.

like image 20
SLaks Avatar answered Oct 05 '22 04:10

SLaks


Because of ambiguity. If you went and did:

T f(params int[] a, params int[] b)

And you called:

f(1, 2, 3, 4)

How would the compiler know where one started and the other began? You could argue that such cases could still be flagged as compilation errors and still allow the unambiguous cases to continue, but with inheritance it could get complicated and it just isn't worth it. Pass two arrays instead. I can't think of any situation that would merit two parameter arrays, and even then, it's syntactic sugar. There's no difference between it and just using arrays instead.

(Another example:

T f(params string[] a, params object[] b);

string inherits from object. This is still ambiguous...)

like image 24
Ry- Avatar answered Oct 05 '22 02:10

Ry-


There are many edge cases that make this an improbable feature. Consider this example:

public static class Coolifier
{
  public static void BeCool<A,B>(params A[] a, params B[] b)
  {
  }
}

Coolifier.BeCool<string,string> ("I", "don't", "work.", "DO", "I", "?");

The example shows how there is no way to know where the first params [] ends and the next begins.

like image 37
agent-j Avatar answered Oct 05 '22 03:10

agent-j


It would pose question and lead to conflicts.

For example, let's say B inherits from A.

How would compiler understand:

f(A1, A2, B1, B2)

Would it mean f({A1, A2}, {B1, B2}) or f({A1, A2, B1}, {B2})?

I think theoretically compiler could be smart and detect conflicts.
However, originally when .NET and C# itself were designed the idea was to try to avoid unambiguous and tricky cases like this.

like image 33
Alex Aza Avatar answered Oct 05 '22 04:10

Alex Aza