5

Suppose, we have a very simple class:

class ObjectList {
    public List<string> List1 { get; } = new List<string>();
    public List<string> List2 { get; set; }
}

and we want to make an instance of this class:

ObjectList objectList = new ObjectList {
    List1 = { "asdf", "qwer" },
    List2 = new List<string> { "zxcv", "1234" }
};

So, in case of List2 it is ok, using "=" we set property. But in List1 case it looks like we set property, but actually, we suppose to set it somewhere before, and here we only set values. And it very similar to array initializing:

string[] arr = { "val1", "val2" }

Why C# uses this confusing syntax here?

Edit: I guess I confused many viewers with C# 6.0 syntax, but it's not the point. Let's use old good C# 3.0 and .net 2.0. And lets add more fun too this and add some values ("1" and "2") to the list from start, as Jeff Mercado recommended:

class Program {
    static void Main(string[] args) {
        ObjectList objectList = new ObjectList {
            List1 = { "asdf", "qwer" },
        };
    }
}
class ObjectList {
    List<string> _List1 = new List<string>() { "1", "2" };
    public List<string> List1 {
        get {
            return _List1;
        }
    }
}

It shows the same weird syntax. And at the end I have list { "1", "2", "asdf", "qwer" }, which is even more confusing. I can expect this the less.

Alex Butenko
  • 3,664
  • 3
  • 35
  • 54
  • 1
    That compiles? List1 doesn't have a public setter, so you shouldn't be able to set it in the initializer syntax. – Jim Mischel Dec 21 '15 at 22:50
  • 1
    It does. The compiler actually converts that syntax into calls to `.Add()` – Carlos Muñoz Dec 21 '15 at 22:52
  • I agree, this syntax is really weird. Semantically, it's more like `+=`. It's exactly the same for nested object initializers - without a constructor call, the initializer will only set properties. – Jakub Lortz Dec 21 '15 at 22:53
  • You can't have `{ get; }` alone without a set. If you want it to return `{ "asdf", "qwer" }` make the get return `new List { "asdf", "qwer" }` – Giora Guttsait Dec 21 '15 at 22:53
  • Well I learned something new today. That's pretty radical. – Jonesopolis Dec 21 '15 at 22:54
  • 1
    Apparently, in the context of an initializer, you're just using the collection initializer syntax and you're just adding more items to the `List1` instance. You can verify that the initial list instance is actually preserved. Try initializing with a non-empty list or check the reference, you'll see it's unchanged. – Jeff Mercado Dec 21 '15 at 22:54
  • This is how it gets converted to `.Add()` calls by the compiler: http://i.stack.imgur.com/XbDRv.png – Carlos Muñoz Dec 21 '15 at 23:01
  • 1
    @HenkHolterman that doesn't explain why that particular syntax was chosen. The OP is already aware of the behavior – Carlos Muñoz Dec 21 '15 at 23:01
  • BTW, collection initializers were introduced in C# 3 not 6: https://msdn.microsoft.com/en-us/library/bb384062(v=vs.100).aspx – Carlos Muñoz Dec 21 '15 at 23:22
  • As a further test, try changing the property to a collection that doesn't provide an `Add()` method (e.g., an array). You could even view the expression trees for the expressions (either the Roslyn or LINQ trees). You'll see it's clearly using collection initializers. – Jeff Mercado Dec 22 '15 at 00:20

2 Answers2

3

Why C# uses this confusing syntax here?

You are right that it's a little bit weird, but that's because you are mixing 2 principles. As several comments already pointed out, the { item1, items2 } syntax is converted to .Add(itemN) calls when the item on the left of the = is a Collection.

So the weirdness is a result of the fact that

List<SomeClass> list = new List<SomeClass> { item1, item2 };
SomeClass[] array = { item1, item2 };

are treated differently.

The other part is that your sample moves the new List<SomeClass> around, but it is there in both cases.

H H
  • 263,252
  • 30
  • 330
  • 514
  • 1
    I'd rather say that the weirdness is a result of an assignment operator (`=`) not doing any assignment. – Jakub Lortz Dec 21 '15 at 23:22
  • I still don't have the whole picture. C#, of all the languages. looks very neat, but assignment doing .add() looks like a bug in the language. There had to be a very serious reason for the C# team to add this, and I'd like to know it. – Alex Butenko Dec 21 '15 at 23:39
2

The answer has been given by Eric Lippert in his answer to Why is an Add method required for { } initialization?

The by-design goal motivated by typical usage scenarios for collection initializers was to make initialization of existing collection types possible in an expression syntax so that collection initializers could be embedded in query comprehensions or converted to expression trees.

Every other scenario was lower priority; the feature exists at all because it helps make LINQ work.

So it seems that, even when this syntax makes more sense when creating readonly collections, it was added anyway so they can deliver LINQ on time.

Community
  • 1
  • 1
Carlos Muñoz
  • 17,397
  • 7
  • 55
  • 80
  • Hmm.. Interesting story. But how it helps in LINQ? – Alex Butenko Dec 22 '15 at 00:32
  • It says it right ther in the quote: "make initialization of existing collection types possible in an expression syntax" – Carlos Muñoz Dec 22 '15 at 00:34
  • Can you provide an example? I think I never used it that way, so I have troubles to visualize it. – Alex Butenko Dec 22 '15 at 00:40
  • @AlexButenko Consider this code: `var q = from x in someCollection select new ObjectList { List1 = { "qwerty", "asdfgh" } }` where `someCollection` is `IEnumerable` it would be impossible to declare it in one expression without using `let` or multiple statements enclosed in `{ ... }` – Carlos Muñoz Dec 22 '15 at 01:26
  • I still don't see any reason to use "=" and not some other symbol or keyword. Actually, I don't even see any strong reason to have it at all, why not just use syntax as for List2. – Alex Butenko Dec 22 '15 at 01:55
  • 2
    @AlexButenko: Because you want to be able to distinguish between the cases "I am adding to an existing list" and "I would like a new list created with these items". I agree that this is a subtle and somewhat confusing difference, but the language needed to have a way to express both those concepts. – Eric Lippert Dec 22 '15 at 03:17