19

Valid syntax:

var test = new List<string>
{
   "a",
   "b",
   "c",//Valid trailing comma
};

Invalid syntax:

private void Test(params string[] args)
{
}

Test(
   "a",
   "b",
   "c",//Invalid trailing comma
);

Is it a matter of syntax inconsistency or a calculated decision?

VLAZ
  • 26,331
  • 9
  • 49
  • 67
Den
  • 1,827
  • 3
  • 25
  • 46
  • In the second example, the compiler uses the commas to separate method arguments, not collection items. `params` is handled separately. On method arguments you have never been able to leave a trailing comma. – Adam Houldsworth Nov 11 '14 at 12:40
  • 1
    The compiler is not a human, it doesn't know and doesn't care that you want to pass a collection to the method. It just "sees" that the syntax is not valid. The former syntax is valid because it is allowed in the language specification to initialize collections. – Tim Schmelter Nov 11 '14 at 12:42
  • 3
    This question was incorrectly closed as a duplicate. [The linked question](http://stackoverflow.com/questions/3675173/why-can-you-have-a-comma-at-the-end-of-a-collection-initializer) answered "why are we allowed trailing commas on collection initialisers", whereas this is asking "why are we not allowed trailing commas for `params` method arguments". That said, answers will be similar - but the question is not. – Adam Houldsworth Nov 11 '14 at 12:42
  • The difference is that method calls look the same with or without `params`. Technically probably it would be possible, but `params` and initializers were added later. – BartoszKP Nov 11 '14 at 12:43
  • 2
    This is why commas are allowed in the first case: http://stackoverflow.com/questions/2147333/net-enumeration-allows-comma-in-the-last-field/2147344#2147344 I think the difference is because when initializing a list, you are listing the same kind of elements, but when calling a function, you can have anything there, so the syntax is much more strict. – Andrew Nov 11 '14 at 12:45
  • @BartoszKP: this would never be implemented since the costs outweigh the benefit. It would even introduce new pitfalls. – Tim Schmelter Nov 11 '14 at 12:46
  • @TimSchmelter That's subjective, although I agree. I don't even like trailing commas in initializers :) – BartoszKP Nov 11 '14 at 12:47
  • I like trailing commas when I can get away with it because it removes a little annoyance with merging changes, it is also why I favour new lines for collection and property initialisers regardless of content. – Adam Houldsworth Nov 11 '14 at 12:48

2 Answers2

13

So I'll take a stab at this even though I will never know the "true" reason as I wasn't on the compiler team - and the likelihood of one turning up is questionable.

Trailing commas are generally useful in a few scenarios, namely merging and code-generation. In the context of stuff like collection or property initialisers and enums, leaving a trailing comma is harmless (the compiler can safely infer "end of list" as there is also a closing block bracket it can hook on to.

Method parameters are quite explicit - the compiler needs a lot of control in this area so that it can provide good feedback when people are coding and for other ancillary features. Leaving a trailing comma on method arguments doesn't add any of the value as above and my start to cause confusion over how to handle "incomplete" code (did the user leave it there intentionally or are they just about to type in the next argument?).

You are correct that params fall into the conceptual gap in that you see them as an array, and you can specify them as comma delimited (which was supported prior to collection initialisers). So why do they depart in style from collection initialisers?

The language spec for the params bit doesn't explicitly specify trailing comma support, though it does for collection initialisers for parity towards other languages (C++ I think), which increases familiarity with developers migrating to C# from elsewhere.

My supposition: the fact that it wasn't in spec then causes YAGNI to apply, and from that point forwards the value proposition for the feature is a no-brainer in favour of not implementing it.

silkfire
  • 24,585
  • 15
  • 82
  • 105
Adam Houldsworth
  • 63,413
  • 11
  • 150
  • 187
  • 2
    "did the user leave it there intentionally or are they just about to type in the next argument?" reasoning applies just as well to enums and initializer lists. Also, method calls are also present in generated code, and are also being merged :) – BartoszKP Nov 11 '14 at 12:59
  • 2
    Indeed, but the reasoning is less important because the compiler isn't trying to hook up unknown enum values. I think it falls into the category of minimal value and cost of development. There are definitely arguments for it and I don't argue a case either way. I'm just trying to theorise on why things are the way they are. – Adam Houldsworth Nov 11 '14 at 13:05
  • 1
    The part about "unknown values" in your comment seems to hit the spot on this one. The conceptual gap in your answer also - they seem to be most relevant, other parts are almost noise ;) Although nice attempt on this hard question overall. – BartoszKP Nov 11 '14 at 13:10
  • 1
    @BartoszKP Thanks :-) I do agree with your sentiment though. It seems like it could be supported and falls into the same argument categories as enums et al. Who knows, now that Roslyn is enabling faster iteration and lower dev costs, we may see these debated inconsistencies start to be addressed. – Adam Houldsworth Nov 11 '14 at 14:27
3

if you look at lexical Grammer here

C.2.9 Arrays

array-initializer:
{   variable-initializer-listopt   }

{   variable-initializer-list   ,   }// This comma is causing this

variable-initializer-list:
variable-initializer
variable-initializer-list   ,   variable-initializer

Calling Function is Like this ....MethodName( formal-parameter-listopt );

 formal-parameter-list:
 fixed-parameters
fixed-parameters   ,   parameter-array
parameter-array
fixed-parameters:
fixed-parameter
fixed-parameters   ,   fixed-parameter
fixed-parameter:
attributesopt   parameter-modifieropt   type   identifier
parameter-modifier:
ref
out
parameter-array:
attributesopt   params   array-type   identifier

There is no place for trailing commas because, language is written that way, I don't know the reason but Adam does

Charlie
  • 4,827
  • 2
  • 31
  • 55