The mysterious ways of the params keyword in C#


If a parameter to a C# method is declared with the params keyword, then it can match either itself or a comma-separated list of um itselves(?). Consider:

class Program {
  static void Sample(params int[] ints) {
   for (int i = 0; i < ints.Length; i++) {
    System.Console.WriteLine("{0}: {1}", i, ints[i]);
   }
   System.Console.WriteLine("-----");
  }
  public static void Main() {
   Sample(new int[] { 1, 2, 3 });
   Sample(9, 10);
  }
}

This program prints

0: 1
1: 2
2: 3
-----
0: 9
1: 10
-----

The first call to Sample does not take advantage of the params keyword and passes the array explicitly (formally known as normal form). The second call, however, specifies the integers directly as if they were separate parameters. The compiler generates a call to the function in what the language specification calls expanded form.

Normally, there is no conflict between these two styles of calling a function with a params parameter because only one form actually makes sense.

Sample(new int[] { 0 }); // normal form
Sample(0); // expanded form

The first case must be called in normal form because you cannot convert an int[] to an int; conversely, the second case must be called in expanded form because you cannot convert an int to an int[].

There is no real problem in choosing between the two cases because T and T[] are not implicitly convertible to each other.

Oh wait.

Unless T is object!

class Program {
  static void Sample(params object[] objects) {
   for (int i = 0; i < objects.Length; i++) {
    System.Console.WriteLine("{0}: {1}", i, objects[i]);
   }
   System.Console.WriteLine("-----");
  }
  public static void Main() {
   Sample(new object[] { "hello", "there" });
  }
}

There are two possible interpretations for that call to Sample:

  • Normal form: This is a call to Sample where the objects is an array of length 2, with elements "hello" and "there".

  • Expanded form: This is a call to Sample where the objects is an array of length 1, whose sole element is the array new object[] { "hello", "there" }.

Which one will the compiler choose?

Let's look at the spec.

A function member is said to be an applicable function member with respect to an argument list A when all of the following are true:

  • The number of arguments in A is identical to the number of parameters in the function member declaration.

  • For each argument in A, [blah blah blah], and

    • for a value parameter or a parameter array, an implicit conversion exists from the type of the argument to the type of the corresponding parameter, or

    • [blah blah blah]

For a function member that includes a parameter array, if the function member is applicable by the above rules, it is said to be applicable in normal form. If a function member that includes a parameter array is not applicable in its normal form, the function member may instead be applicable in its expanded form:

...

(I removed some text not relevant to the discussion.)

Note that the language specification prefers normal form over expanded form: It considers expanded form only if normal form does not apply.

Okay, so what if you want that call to be applied in expanded form? You can simulate it yourself, by manually performing the transformation that the compiler would do:

  public static void Main() {
   Sample(new object[] { new object[] { "hello", "there" } });
  }

Yes, it's extra typing. Sorry.

Comments (21)
  1. Programmerman says:

    You can also box the object array:

    public static void Main() {

    Sample((object)(new object[] {"hello", "there"}));

    }

    The parameter passed is now of type System.Object instead of System.Object[]. It can't be implicitly converted back to System.Object[], it can only be explicitly converted. Still extra typing, but less extra typing.

  2. Lindo says:

    Or you could just.. Not do that. Using 'params' on plain Objects is rather bad form, and probably doesn't make sense unless ones application design has already gone off the rails..

  3. Brad says:

    @Lindo – actually I can think of a few use cases where params object[] is quite useful, most especially for things like string.Format and Console.WriteLine. However I agree its best to avoid when at all possible.

  4. Csaboka says:

    Java has the exact same way of using varargs (but with different syntax, of course[1]), with the exact same caveats.

    There is another case, though, that doesn't require T to be Object: specifying null as the last parameter. Null can implicitly be converted to any reference type, so this may mean sending null instead of an actual array instance, or sending an array instance with exactly one null element in it. Just like with Raymond's example, the normal form is preferred, with a warning about ambiguity.

    [1] It always amazes me that the two languages are so close in spirit, but still make every effort to have different syntax for matching features…

  5. Danny says:

    @Csaboka – why? Given the fact that Java was written with intent to have a C/C++ portable everywhere with WORE (something way ahead of it's time, Microsoft did .NET eons after Sun) and that C# creator was coming from Delphi you have your answer. C# aimed to have RAD programming as best as Delphi hence the difference in syntax.

  6. John Doe says:

    @Danny, please justify how the syntax of C# is similar to that of Delphi. And I mean closely similar, not remotely similar.

    Also, please justify how the syntax of C# is so different than that of Java for good reasons.

    Note that the person who brought the Delphi-style things to Microsoft products used to work in Visual J and J# before.

    Really, there are products that unify both languages (heck, they even load the compiled byte-code of one in the other) under the same environment, just because the languages and the VMs and memory models are so similar.

  7. Csaboka says:

    To be fair, Sun/Oracle is just as "guilty" in the different syntaxes as Microsoft is. Every time when Java implemented some feature C# already had, they made damn sure to make it at least cosmetically different.

    When Java got foreach, they made it use the regular "for" keyword.

    When it got "using" blocks, they changed the syntax of "try" blocks instead of calling it "using".

    And of course, what .NET calls IDisposable is called AutoCloseable in Java-land, and instead of Dispose()-ing it, you close() it.

    Oh, and when Java finally gets lambda expressions, of course it will use -> for it instead of =>, just to be different: docs.oracle.com/…/lambdaexpressions.html

  8. Nick says:

    > Oh, and when Java finally gets lambda expressions, of course it will use -> for it instead of =>, just to be different

    I actually laughed out loud when I first read over the Java lambda documentation and saw that.  To be fair, there /might/ be some argument that -> is easier to type than =>, but that might be reaching (ha ha) a bit.

  9. Daniel Neely says:

    @John Doe

    RE your last point, James Iry's history of the two languages is so funny because it's completely true.

    james-iry.blogspot.com/…/brief-incomplete-and-mostly-wrong.html

  10. Vince says:

    Don't let your design get to that stage.

  11. @Nick: one can argue that => looks too much like <= or >=, completely different creatures. Also, C++ uses ->

  12. Ooh says:

    @Programmerman: Just for completeness and correctness, that's not boxing as you're transforming a reference type to another reference type. That's called casting. (Boxing would be the correct terminology if the transformation would go from value type to reference type.)

  13. Mason Wheeler says:

    @John Doe: Sure, the *syntax* looks like Java, but the libraries and the language style, especially in the early .NET releases, are pure Delphi.  It's almost like Microsoft hired Delphi engineers and told them "let's rewrite Delphi to look like Java."

  14. John Doe says:

    @Mason Wheeler, for UpperCamelCase, properties and delegates, sure, C# smells like Delphi.

    Yet, I'm still expecting for proof of more similarity between C# and Delphi than C# and Java.

    Note that Delphi was on-par with C++ (minus macros and templates) back when C#/.NET was introduced. It compiled to machine code instead of byte code, it didn't have garbage collection, etc. If anything, Visual J++ was the spiritual ancestor of C#/.NET before Sun sued Microsoft for infringement by extending Java without consent.

    Or rather, C#/.NET was the response to that (this is just my opinion).

  15. Danny says:

    @Mason Wheeler – <It's almost like Microsoft hired Delphi engineers and told them "let's rewrite Delphi to look like Java.">

    Actually Microsoft did exactly that! As in word for word. There is no "almost"!!

    "Anders Hejlsberg (born December 1960) is a prominent Danish software engineer who co-designed several popular and commercially successful programming languages and development tools. He was the original author of Turbo Pascal and the chief architect of Delphi. He currently works for Microsoft as the lead architect of C# and core developer on TypeScript." Source wikipedia :

    en.wikipedia.org/…/Anders_Hejlsberg

  16. john says:

    Oh, nice article! That explains the weird wrap around "object[]" we need to do to call MemberInfo.Invoke(…, parameters) !

  17. Moneka says:

    Nice article!

    I love the comparisons between Microsoft .Net and Java! So true!

  18. John Doe says:

    @Danny, it was the other way around, "let's make Java a bit more like Delphi". Hence the litigation with Sun.

    Since we're quoting Wikipedia here:

    … build a new language at the time called Cool, which stood for "C-like Object Oriented Language".

    … the originator of Java, called C# an "imitation" of Java;

    … (authors of a C++ streams book) stated in a blog post that "Java and C# are almost identical programming languages

    … C# borrowed a lot from Java – and vice versa. Now that C# supports boxing and unboxing, we'll have a very similar feature in Java.

    … Anders Hejlsberg has argued that C# is "not a Java clone" and is "much closer to C++" in its design.

    in en.wikipedia.org/…/C_Sharp_(programming_language) (references included)

  19. Danny says:

    @John Doe <…"let's make Java a bit more like Delphi"…>

    Java – source : en.wikipedia.org/…/Java_(programming_language)

    – James Gosling, Mike Sheridan, and Patrick Naughton initiated the Java language project in June 1991

    – Sun Microsystems released the first public implementation as Java 1.0 in 1995

    Delphi – source : en.wikipedia.org/…/Embarcadero_Delphi

    – Delphi (later known as Delphi 1) was released in 1995 for the 16-bit Windows 3.1

    This sounds to me that Java was developed in parallel with Delphi, not that Java is a Delphi clone for cross-platform. And my point was that C# is a cow-helicopter of Delphi/Java. It has the RAD of Delphi (or can give milk like a cow) and the cross-platform (within Windows world) achievement (because is a .NET technology) (or can fly like a helicopter). It's a monster? Definitely! It's good? Hell yeah. Now these days Delphi is playing catch-up with cross platform trying to beat Java at it by making native code. 2 More steps (Android and Linux) and will be there.

    Let's take an example of Delphi RAD. Make a clone of Notepad. In Delphi can be achieved in maximum one hour. Dump 2 visual controls (Menu and Memo), create the menu entries, write the handlers. Before C# was any of the .NET programming languages able to do that? VB.NET could but was a hell lot more time then in Delphi (C++.NET was a miss from start). Don't believe me? Create a youtube movie with you doing exactly that in VB.NET (or C++.NET) and then I'll show you mine doing it in Delphi. Do same in Java? I'll pity you.

    What I am trying to say is that in land of RAD Delphi is the king, while in the land of cross-platform Java is the king. C#, in it's cross-platform Windows world, tries to combine a cow with a helicopter. Hats off, it's there. Of course is not perfect. And that Delphi road-map shows cross-platform on the major ones is 2 steps away which will make Java obsolete (or only web applications relevant). C# will never be a true cross-platform because Microsoft is like Spanish Inquisition regarding worlds outside of Windows, and to those who chant "Mono!" I say "Get serious!".

  20. Gabe says:

    Danny: I would posit that C# exists for the primary reason that, as you observed, it is so difficult to make a RAD environment with Java. MS wanted to make a RAD Java environment that would be as easy to use as VB, so they added a few things to Java (most notably delegates) and called it J++. Sun called foul and sued, forcing MS to abandon Java.

    As a result, MS had to create their own language to provide a RAD environment for C/C++ programmers. Hence, we have C# today.

  21. >… the originator of Java, called C# an "imitation" of Java;

    As someone who has spent who knows how many hours with both (as I'm sure you all have), they are different enough that (to me anyway) C# is *much* more productive. As other's have said, Java is not so much an object-orientated language, as it is a class-orientated language – in Java the answer to everything is a new class – whether it be outer, inner (ick), static inner, or anonymous –  Long-time C# features such as delegates/events, lambdas, LINQ, using, iterators, decent initialization syntax, partial class, anonymous types and type inference, namespaces as a separate concept from file organization (no Nazi regimentation of single-class forced into directories)  – all add up to a much more natural transformation from idea to code.

Comments are closed.