Why can I initialize a List like an array in C#?

C#.NetListInitialization

C# Problem Overview


Today I was surprised to find that in C# I can do:

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

Why can I do this? What constructor is called? How can I do this with my own classes? I know that this is the way to initialize arrays but arrays are language items and Lists are simple objects ...

C# Solutions


Solution 1 - C#

This is part of the collection initializer syntax in .NET. You can use this syntax on any collection you create as long as:

  • It implements IEnumerable (preferably IEnumerable<T>)

  • It has a method named Add(...)

What happens is the default constructor is called, and then Add(...) is called for each member of the initializer.

Thus, these two blocks are roughly identical:

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

And

List<int> temp = new List<int>();
temp.Add(1);
temp.Add(2);
temp.Add(3);
List<int> a = temp;

You can call an alternate constructor if you want, for example to prevent over-sizing the List<T> during growing, etc:

// Notice, calls the List constructor that takes an int arg
// for initial capacity, then Add()'s three items.
List<int> a = new List<int>(3) { 1, 2, 3, }

Note that the Add() method need not take a single item, for example the Add() method for Dictionary<TKey, TValue> takes two items:

var grades = new Dictionary<string, int>
    {
        { "Suzy", 100 },
        { "David", 98 },
        { "Karen", 73 }
    };

Is roughly identical to:

var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;

So, to add this to your own class, all you need do, as mentioned, is implement IEnumerable (again, preferably IEnumerable<T>) and create one or more Add() methods:

public class SomeCollection<T> : IEnumerable<T>
{
    // implement Add() methods appropriate for your collection
    public void Add(T item)
    {
        // your add logic    
    }

    // implement your enumerators for IEnumerable<T> (and IEnumerable)
    public IEnumerator<T> GetEnumerator()
    {
        // your implementation
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Then you can use it just like the BCL collections do:

public class MyProgram
{
    private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };    

    // ...
}

(For more information, see the MSDN)

Solution 2 - C#

It is so called syntactic sugar.

List<T> is the "simple" class, but compiler gives a special treatment to it in order to make your life easier.

This one is so called collection initializer. You need to implement IEnumerable<T> and Add method.

Solution 3 - C#

According to the C# Version 3.0 Specification "The collection object to which a collection initializer is applied must be of a type that implements System.Collections.Generic.ICollection for exactly one T."

However, this information appears to be inaccurate as of this writing; see Eric Lippert's clarification in the comments below.

Solution 4 - C#

It works thanks to collection initializers which basically require the collection to implement an Add method and that will do the work for you.

Solution 5 - C#

Another cool thing about collection initializers is that you can have multiple overloads of Add method and you can call them all in the same initializer! For example this works:

public class MyCollection<T> : IEnumerable<T>
{
    public void Add(T item, int number)
    {

    }
    public void Add(T item, string text) 
    {

    }
    public bool Add(T item) //return type could be anything
    {

    }
}

var myCollection = new MyCollection<bool> 
{
    true,
    { false, 0 },
    { true, "" },
    false
};

It calls the correct overloads. Also, it looks for just the method with name Add, the return type could be anything.

Solution 6 - C#

The array like syntax is being turned in a series of Add() calls.

To see this in a much more interesting example, consider the following code in which I do two interesting things that sound first illegal in C#, 1) setting a readonly property, 2) setting a list with a array like initializer.

public class MyClass
{	
    public MyClass()
    {	
        _list = new List<string>();
    }
    private IList<string> _list;
    public IList<string> MyList 
    { 
        get
        { 
            return _list;
        }
    }
}
//In some other method
var sample = new MyClass
{
    MyList = {"a", "b"}
};

This code will work perfectly, although 1) MyList is readonly and 2) I set a list with array initializer.

The reason why this works, is because in code that is part of an object intializer the compiler always turns any {} like syntax to a series of Add() calls which are perfectly legal even on a readonly field.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIgnacio Soler GarciaView Question on Stackoverflow
Solution 1 - C#James Michael HareView Answer on Stackoverflow
Solution 2 - C#KrizzView Answer on Stackoverflow
Solution 3 - C#Olivier Jacot-DescombesView Answer on Stackoverflow
Solution 4 - C#vc 74View Answer on Stackoverflow
Solution 5 - C#nawfalView Answer on Stackoverflow
Solution 6 - C#yoel halbView Answer on Stackoverflow