Runtime: C# new operate bug?

Created on 20 Aug 2017  路  5Comments  路  Source: dotnet/runtime

```c#
public class ListTest
{
public List MyList;
public ListTest()
{
MyList = new List { 1, 2, 3 };
}
}

var listTest = new ListTest()
{
MyList = {4,5,6}
};
```

Do you know the value of listTest.MyList ???

It would be {1,2,3,4,5,6} (on .net framework 4.6.1)

Someone can explain that??

[EDIT] Fixed proper code formatting by @karelz

area-Meta question

Most helpful comment

It's better to raise error without new.

That would block a very common scenario - initializing readonly collection properties:
C# public class ListTest { public List<int> MyList { get; } = new List<int>(); } var listTest = new ListTest { // You can't do = new List<int> { 4, 5, 6 } here because the property is readonly MyList = { 4, 5, 6 } };

All 5 comments

This is really a C# language question. Collection initialization works by calling collection's Add method so your MyList = {4,5,6} is really:
c# listTest.MyList.Add(4); listTest.MyList.Add(5); listTest.MyList.Add(6);
But more importantly:

C# new operate bug?

What new? There's no new in MyList = {4,5,6} :smile: So there's no reason to expect a new list containing 4, 5 and 6 to be created.

[EDIT] Added C# syntax highlight by @karelz

It's really a big pit ! Someone would forget new , but they think they have done that. It's better to raise error without new .

It's better to raise error without new.

That would block a very common scenario - initializing readonly collection properties:
C# public class ListTest { public List<int> MyList { get; } = new List<int>(); } var listTest = new ListTest { // You can't do = new List<int> { 4, 5, 6 } here because the property is readonly MyList = { 4, 5, 6 } };

From the C#5 spec (all specs after initializers were introduced have similar language):

A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.

A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property. The field or property must be of a collection type that satisfies the requirements specified in 搂7.6.10.3.

So the code:

```c#
var listTest = new ListTest()
{
MyList = {4,5,6}
};


Should be equivalent to:

```c#
var listTest = new ListTest();
listTest.MyList.Add(4);
listTest.MyList.Add(5);
listTest.MyList.Add(6);

So listTest.MyList should contain 1, 2, 3, 4, 5, 6. Anything else would be a bug.

It's really a big pit ! Someone would forget new, but they think they have done that. It's better to raise error without new.

People can forget all manner of things, but if the definition of ListTest defaults to setting a MyList to {1, 2, 3} then presumably there's a reason for that. It's just as sensible to use that as a starting point to then add more numbers as it is to use new to set something completely different, and the language allows both to be expressed.

The code is also analogous to the way that when using a collection initialiser on its own first either new TheType() is called or whatever constructor is explicitly used, and then the further items added. E.g.:

```c#
var starter = new[] { 1, 2, 3 };
var list = new List(starter) { 4, 5, 6 };
foreach (int item in list)
{
Console.WriteLine(item);
}

Outputs the numbers 1 through 6.

Ultimately though the biggest problem would be that if nested collection initialisers didn't go through the constructor included in `ListTest` would break encapsulation. Consider:

```c#
public class ListTest
{
    public readonly AddOnlyList<int> MyList;
    public ListTest()
    {
        MyList = new AddOnlyList<int> { 1, 2, 3 };
    }
}

It is part of the class invariant of ListTest that MyList starts with 1, 2, 3. If outside code is allowed to break the encapsulation and force other values into it then ListTest no longer controls its own invariants and can no longer be reasoned about by its author.

Also, there's no reason why a type with Add() that implements IEnumerable need have a parameterless constructor, so no reason why having the code in the example equivalent to new TheListType { 4, 5, 6 } should even be valid.

[EDIT] Fixed C# syntax highlighting by @karelz

@JonHanna,
Thanks for your greate answer锛孖 understand now !!!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aggieben picture aggieben  路  3Comments

noahfalk picture noahfalk  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

bencz picture bencz  路  3Comments

matty-hall picture matty-hall  路  3Comments