3

Why is it possible to do the following without compiler-errors:

private class Foo
{
     public List<string> Bar { get; set; }
}

private void SomeRandomMethod(){
    var test2 = new Foo
    {
         Bar = { "test", "test2" }
    };
}

But this doesn't work?

List<string> test = { "test", "test2" };

I do know that i can write new { "test", "test2" }; and then it works. I don't want a workaround or a solution or something similar for this question, instead I'd like to have an explaination what the difference in those two cases exactly is.

EDIT: When changing the types from List<string> to string[] the first example doesn't compile anymore but the 2nd one does.

Thanks!

19
  • Possible duplicate of I don't understand why we need the 'new' keyword Commented Aug 24, 2018 at 22:48
  • 2
    It's called a nested object initializer. See this question. I agree the syntax is counterintuitive. Commented Aug 24, 2018 at 22:53
  • 1
    @JohnWu The funny thing is: When i execute the first code snipped it crashes with a nullreferenceexception because the list is null and it seems that the add method is called for each value in the list i provide Commented Aug 24, 2018 at 22:56
  • 1
    @Nkosi Ahh ok, so when i use {"test"} in a "normal" scope then it's interpreted as an array initializer (which creates a new instance of an array) and when i use it inside of an object initializer it's interpreted as collection initializer and therefore calls the Add-Method of the collection instead of creating a new instance of it - did I get this right? Commented Aug 24, 2018 at 23:08
  • 1
    @Dominik now you're getting it. Yes. Commented Aug 24, 2018 at 23:10

1 Answer 1

4

The confusion here comes from the same syntax being treated as different in different contexts. In the object initializer, the expression represents a collection initializer.

The spec defines collection initializer as:

7.6.10.3 Collection initializers

A collection initializer consists of a sequence of element initializers, enclosed by { and } tokens and separated by commas. Each element initializer specifies an element to be added to the collection object being initialized, and consists of a list of expressions enclosed by { and } tokens and separated by commas.

Later in the same chapter:

For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation.

So this is why you get NRE in the first example. Since you don't add a new List<string> before using the collection initializer, you are attempting to call Add on a null object. You will either need to add new List<string> or do it in the constructor.

An array initializer is different, when you omit new there the compiler just adds new for you, so this:

string[] values = { "foo", "bar" };

is equivalent to:

string[] values = new [] { "foo", "bar" };

When you use { "foo", "bar" } outside of the object initializer you get the error because you are trying to assign an array to a list.

Note: In the collection initializer, you might expect the compiler to be smarter and add the new for you as in the array initializer but it might have other side effects. For example, if you are initializing the list in the constructor, it would overwrite the list. So I guess that's why it's the way it is.

Sign up to request clarification or add additional context in comments.

2 Comments

Phew...you saved me having to write this up. up vote to you kind sir ;P
@Nkosi haha, np :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.