Why can I initialize a List like an array in C#?
C#.NetListInitializationC# 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
(preferablyIEnumerable<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
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.