List <int> test = {1, 2, 3} - is this a function or an error?

As you know, it is not allowed to use Array initialization syntax with lists. This will give a compile time error. Example:

List<int> test = { 1, 2, 3} // At compilation the following error is shown: // Can only use array initializer expressions to assign to array types. 

However, today I did the following (very simplified):

 class Test { public List<int> Field; } List<Test> list = new List<Test> { new Test { Field = { 1, 2, 3 } } }; 

The above code compiles just fine, but when run it will give an error message "Object does not refer to an object."

I would expect the code to give a compile-time error. My question to you is: why is this not the case, and are there any good reasons when such a scenario will work correctly?

This has been tested using .NET 3.5, both .Net and Mono compilers.

Greetings.

+47
c # compiler-errors
Feb 04 2018-11-14T00:
source share
6 answers

I think this is behavioral behavior. Test = { 1, 2, 3 } compiled into code that calls the Add method of the list stored in the Test field.

The reason you get a NullReferenceException is because Test is null . If you initialize the Test field in a new list, then 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 } }; 

This is quite logical - if you write new List<int> { ... } , then it creates a new copy of the list. If you do not add an object construct, it will use the existing instance (or null ). As far as I can see, the C # specification does not contain an explicit translation rule that matches this scenario, but it gives an example (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.

+43
Feb 04 2018-11-14T00:
source share

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

Since your expectation is contrary to specification and implementation, your expectation will be unfulfilled.

Why doesn't it work at compile time?

Since the specification specifically states that this is legal in section 7.6.10.2, which I quote here for your convenience:




The member initializer, which sets the collection initializer after the equal sign, is the initialization of the built-in collection. Instead of assigning a new collection to a field or property, the items specified in the initializer are added to the collection referenced by the field or property.




if such code worked correctly?

As the specification says, the elements specified in the initializer are added to the collection referenced by the property. The property does not reference the collection; it is zero. Therefore, at runtime, it throws an exception using NULL. Someone must initialize the list. I would recommend changing the Test class so that its constructor initializes the list.

What scenario motivates this feature?

LINQ queries require expressions, not statements. Adding a member to a newly created collection in a newly created list requires an Add call. Since "Add" returns to void, a call to it can only appear in the expression. This function allows you to either create a new collection (with "new"), or fill it, or fill in an existing collection (without "new"), where the collection is a member of the object that you create as a result of LINQ query.

+25
Feb 04 '11 at 15:05
source share

This code:

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

Translated to this:

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

Since Field is null , you get a NullReferenceException .

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

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

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

 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 it all starts with the expression new . Inside the expression, you can use the collection initializer without 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 you really miss is some kind of literal in the list, which would be very convenient. I suggested one such literal for enumerations here .

+18
Feb 04 '11 at 2:56 a.m.
source share
 var test = (new [] { 1, 2, 3}).ToList(); 
+3
Feb 04 '11 at
source share

The reason for this is that the second example is a member list initializer, and the MemberListBinding expression from System.Linq.Expressions gives an idea of ​​this - for more details see my answer to this other question: What are some examples of MemberBinding LINQ expressions?

This type of initializer requires that the list has already been initialized so that the sequence you add can be added to it.

As a result - syntactically there is nothing wrong with the code - NullReferenceException is a runtime error caused by the fact that the List was not actually created. The default constructor, which is a new list or a built-in new in the body of the code, will solve a runtime error.

As for why there is a difference between this and the first line of code - in your example, this is not allowed, because this type of expression cannot be on the right side of the destination, because it actually does not create anything, this is only a shorthand for Add .

+2
Feb 04 2018-11-11T00:
source share

Change the code as follows:

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

The reason is that you must explicitly create a collection object before you can put elements into it.

+1
Feb 04 '11 at 2:52
source share



All Articles