C# Collection Initializers: Simplify Object Creation

Collection initializers in C# provide a concise, readable syntax for instantiating collections and populating them with elements in a single step. Introduced in C# 3.0, this language feature eliminates the need for repetitive, boilerplate code that previously required creating an empty collection and manually calling insertion methods. By streamlining how lists, dictionaries, and custom collections are populated, collection initializers make codebases cleaner, easier to maintain, and more declarative.

The Problem with Traditional Initialization

Before collection initializers, instantiating and populating a collection required separate statements. Developers first had to instantiate the collection object and then repeatedly call its .Add() method.

// Traditional approach
List<string> fruits = new List<string>();
fruits.Add("Apple");
fruits.Add("Banana");
fruits.Add("Cherry");

While functional, this approach is verbose and clutters the code, especially when dealing with nested collections or setting up test data.

The Collection Initializer Syntax

Collection initializers streamline this workflow by allowing you to specify elements within enclosing braces {} directly after the constructor call.

// Collection initializer approach
List<string> fruits = new List<string> { "Apple", "Banana", "Cherry" };

Under the hood, the C# compiler transforms this syntax into standard .Add() calls during compilation. The runtime performance remains identical to the traditional approach, but the source code becomes significantly more compact.

How Collection Initializers Work Under the Hood

For a class to support standard collection initializers, it must satisfy two requirements:

  1. It must implement the System.Collections.IEnumerable interface.
  2. It must provide an accessible, non-static Add method (or, since C# 6, an accessible extension method named Add).

Because of this mechanism, collection initializers also work seamlessly with complex objects:

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

List<User> users = new List<User>
{
    new User { Name = "Alice", Age = 30 },
    new User { Name = "Bob", Age = 25 }
};

Initializing Dictionaries and Key-Value Collections

Collection initializers handle key-value pairs cleanly. C# offers two primary styles for dictionaries:

Nested Braces Syntax

var scores = new Dictionary<string, int>
{
    { "Alice", 95 },
    { "Bob", 80 }
};

Index Initializer Syntax (C# 6+)

C# 6 introduced index initializers, which allow assigning values directly to keys using indexer notation:

var scores = new Dictionary<string, int>
{
    ["Alice"] = 95,
    ["Bob"] = 80
};

The index syntax uses the dictionary's indexer (scores["Alice"] = 95), which overwrites existing keys rather than throwing an exception if a duplicate key is introduced during initialization.

Key Benefits

  • Improved Readability: Data definitions are declarative and clear, showing the collection's structure and contents immediately.
  • Inline Usability: Collections can be created and passed directly into method calls without needing temporary local variables.
  • Consistency: Combines seamlessly with object initializers to create deep, nested object graphs in a single, coherent statement.