[ACCEPTED]-List<int> test = {1, 2, 3} - is it a feature or a bug?-compiler-errors

Accepted answer
Score: 49

I think this is a by-design behavior. The 12 Test = { 1, 2, 3 } is compiled into code that calls Add method 11 of the list stored in the Test field.

The reason 10 why you're getting NullReferenceException is that Test is null. If you 9 initialize the Test field to a new list, then 8 the code will work:

class Test {    
  public List<int> Field = new List<int>(); 
}  

// Calls 'Add' method three times to add items to 'Field' list
var t = new Test { Field = { 1, 2, 3 } };

It is quite logical - if 7 you write new List<int> { ... } then it creates a new instance 6 of list. If you don't add object construction, it 5 will use the existing instance (or null). As 4 far as I can see, the C# spec doesn't contain 3 any explicit translation rule that would 2 match this scenario, but it gives an example 1 (see Section 7.6.10.3):

A List<Contact> can be created and initialized as follows:

var contacts = new List<Contact> {
    new Contact {
        Name = "Chris Smith",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }
    },
    new Contact {
        Name = "Bob Harris",
        PhoneNumbers = { "650-555-0199" }
    }
};

which has the same effect as

var contacts = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
contacts.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
contacts.Add(__c2);

where __c1 and __c2 are temporary variables that are otherwise invisible and inaccessible.

Score: 25

I would expect that code to give a compile-time 33 error.

Since your expectation is contrary 32 to both the specification and the implementation, your 31 expectation is going to go unfulfilled.

Why 30 doesn't it fail at compile time?

Because 29 the specification specifically states that 28 is legal in section 7.6.10.2, which I quote 27 here for your convenience:


A member initializer 26 that specifies a collection initializer 25 after the equals sign is an initialization 24 of an embedded collection. Instead of assigning 23 a new collection to the field or property, the 22 elements given in the initializer are added 21 to the collection referenced by the field 20 or property.


when would such code run correctly?

As 19 the spec says, the elements given in the 18 initializer are added to the collection 17 referenced by the property. The property 16 does not reference a collection; it is null. Therefore 15 at runtime it gives a null reference exception. Someone 14 has to initialize the list. I would recommend 13 changing the "Test" class so that its constructor 12 initializes the list.

What scenario motivates 11 this feature?

LINQ queries need expressions, not 10 statements. Adding a member to a newly-created 9 collection in a newly-created list requires 8 calling "Add". Since "Add" is void-returning, a 7 call to it can only appear in an expression 6 statement. This feature allows you to either 5 create a new collection (with "new") and 4 populate it, or populate an existing collection 3 (without "new"), where the collection is 2 a member of an object you are creating as 1 the result of a LINQ query.

Score: 18

This code:

Test t = new Test { Field = { 1, 2, 3 } };

Is translated to this:

Test t = new Test();
t.Field.Add(1);
t.Field.Add(2);
t.Field.Add(3);

Since Field is 15 null, you get the NullReferenceException.

This is called a collection initializer, and it 14 will work in your initial example if you 13 do this:

List<int> test = new List<int> { 1, 2, 3 };

You really need to new up something 12 in order to be able to use this syntax, i.e., a 11 collection initializer can only appear in 10 the context of an object creation expression. In 9 the C# spec, section 7.6.10.1, this is the 8 syntax for an object creation expression:

object-creation-expression:
  new type ( argument-list? ) object-or-collection-initializer?
  new type object-or-collection-initializer
object-or-collection-initializer:
  object-initializer
  collection-initializer

So 7 it all starts with a new expression. Inside 6 the expression, you can use a collection 5 initializer without the new (section 7.6.10.2):

object-initializer:
  { member-initializer-list? }
  { member-initializer-list , }
member-initializer-list:
  member-initializer
  member-initializer-list , member-initializer
member-initializer:
  identifier = initializer-value
initializer-value:
  expression
  object-or-collection-initializer // here it recurses

Now, what 4 you're really missing is some kind of list 3 literal, which would be really handy. I 2 proposed one such literal for enumerables 1 here.

Score: 3
var test = (new [] { 1, 2, 3}).ToList();

0

Score: 2

The reason for this is that the second example 19 is a member list initialiser - and the MemberListBinding 18 expression from System.Linq.Expressions 17 give an insight into this - please see my 16 answer to this other question for more detail: What are some examples of MemberBinding LINQ expressions?

This 15 type of initialiser requires that the list 14 is already initialised, so that the sequence 13 you provide can be added to it.

As a result 12 - syntactically there is absolutely nothing 11 wrong with the code - the NullReferenceException is a runtime 10 error caused by the List not actually having 9 been created. A default constructor which 8 news the list, or an inline new in the code body, will 7 solve the runtime error.

As for why there 6 is a difference between that and the first 5 line of code - in your example it's not 4 allowed because this type of expression 3 can't be on the right hand side of an assignment 2 because doesn't actually create anything, it's 1 only shorthand for Add.

Score: 1

Change your code to this:

class Test
{
   public List<int> Field = new List<int>();
}

The reason is that 2 you must explicitly create a collection 1 object before you can put items to it.

More Related questions