Index Initialisers (C# 6)

Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub
C# 6 introduces a new feature called Dictionary Initialisers or Index Initialisers, depending on how they function internally. Even the C# team isn’t consistent—the roadmap page](https://github.com/dotnet/roslyn/wiki/Languages-features-in-C%23-6-and-VB-14) refers to them as "dictionary," while the [feature description document calls them "index." Until the name is settled, what are index initialisers, and how do they work internally?
Note: This is based on VS 2015 CTP 6 and may change.
Adding to Collections in C# 2
In C# 2, the only way to add items to a collection was by using the collection’s built-in methods, typically Add. For example, below we add items to a dictionary and a list using the C# 2 approach.
public static void CSharp_Two_Dictionary()
{
var config = new Dictionary<int, string>();
config.Add(1, "hi");
config.Add(2, "how are you");
config.Add(3, "i am fine");
config.Add(4, "good bye");
foreach (var item in config)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}
}
public static void CSharp_Two_List()
{
var config = new List<string>();
config.Add("hi");
config.Add("how are you");
config.Add("i am fine");
config.Add("good bye");
foreach (var item in config)
{
Console.WriteLine(item);
}
}
Collection Initialisers in C# 3
C# 3 introduced collection initialisers, allowing items to be added using braces. The constructor parentheses become optional. While this is just syntactic sugar—the compiler rewrites it to use the collection’s Add method—the result is cleaner and less error-prone.
The requirements for collection initialisers are:
- The collection must implement IEnumerable.
- The collection must have an
Addmethod (uniquely in .NET, no interface is required—just the method name and at least one parameter).
public static void CSharp_Three_Dictionary()
{
var config = new Dictionary<int, string>
{
{ 1, "hi" },
{ 2, "how are you" },
{ 3, "i am fine" },
{ 4, "good bye" },
};
foreach (var item in config)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}
}
public static void CSharp_Three_List()
{
var config = new List<string>
{
"hi",
"how are you",
"i am fine",
"good bye",
};
foreach (var item in config)
{
Console.WriteLine(item);
}
}
Index Initialisers in C# 6
C# 6 introduces a new way to add items to collections, using index initialisers. The syntax differs slightly:
public static void CSharp_Six_Dictionary()
{
var config = new Dictionary<int, string>
{
[1] = "hi",
[2] = "how are you",
[3] = "i am fine",
[4] = "good bye",
};
foreach (var item in config)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}
}
public static void CSharp_Six_List()
{
// Note: This will throw an error
var config = new List<string>
{
[1] = "hi",
[2] = "how are you",
[3] = "i am fine",
[4] = "good bye",
};
foreach (var item in config)
{
Console.WriteLine(item);
}
}
Key Difference:
Unlike collection initialisers, index initialisers do not use Add. Instead, they leverage the collection’s indexer and setter. The compiled code resembles this:
public static void CSharp_Six_Dictionary_Generated()
{
var config = new Dictionary<int, string>();
config[1] = "hi";
config[2] = "how are you";
config[3] = "i am fine";
config[4] = "good bye";
foreach (var item in config)
{
Console.WriteLine("{0} = {1}", item.Key, item.Value);
}
}
public static void CSharp_Six_List_Generated()
{
// Note: This will throw an exception
var config = new List<string>();
config[1] = "hi"; // Throws ArgumentOutOfRangeException
config[2] = "how are you";
config[3] = "i am fine";
config[4] = "good bye";
foreach (var item in config)
{
Console.WriteLine(item);
}
}
Requirements:
For index initialisers to work, the class must have an indexer with a setter. Unlike collection initialisers, this does not require the class to implement IEnumerable or have an Add method.
Example: Non-Collection Usage
This feature isn’t limited to collections. Let’s assign roles to a team without using constructors.
C# 2 Approach:
public static void CSharp_Two_Set_Members()
{
var team = new Team();
team.ScrumMaster = new Member("Jim");
team.ProjectOwner = new Member("Sam");
team.DevLead = new Member("Phil");
team.Dev = new Member("Scott");
}
// Role enum (unchanged in later examples)
enum Role
{
ScrumMaster,
ProjectOwner,
DevLead,
Dev
}
class Team
{
public Member ScrumMaster { get; set; }
public Member ProjectOwner { get; set; }
public Member DevLead { get; set; }
public Member Dev { get; set; }
}
class Member
{
public Member(string name)
{
Name = name;
}
public string Name { get; }
}
C# 3: Object Initialisers
public static void CSharp_Three_Object_Initialiser()
{
var team = new Team
{
ScrumMaster = new Member("Jim"),
ProjectOwner = new Member("Sam"),
DevLead = new Member("Phil"),
Dev = new Member("Scott"),
};
}
C# 6: Index Initialisers
To use index initialisers, we add an indexer to the Team class:
public static void CSharp_Six_Index_Initialiser()
{
var team = new Team
{
[Role.ScrumMaster] = new Member("Jim"),
[Role.ProjectOwner] = new Member("Sam"),
[Role.DevLead] = new Member("Phil"),
[Role.Dev] = new Member("Scott"),
};
}
class Team
{
public Member this[Role role]
{
get
{
switch (role)
{
case Role.ScrumMaster: return ScrumMaster;
case Role.ProjectOwner: return ProjectOwner;
case Role.DevLead: return DevLead;
case Role.Dev: return Dev;
default: throw new IndexOutOfRangeException();
}
}
set
{
switch (role)
{
case Role.ScrumMaster: ScrumMaster = value; break;
case Role.ProjectOwner: ProjectOwner = value; break;
case Role.DevLead: DevLead = value; break;
case Role.Dev: Dev = value; break;
}
}
}
public Member ScrumMaster { get; private set; }
public Member ProjectOwner { get; private set; }
public Member DevLead { get; private set; }
public Member Dev { get; private set; }
}
This explains how index initialisers work in C# 6 and showcases their flexibility beyond collections. Got ideas on other uses? Share them in the comments!