C# Collection Initializer vs Object Initializer
c# collection initializer vs object initializer: Understand the difference between C# collection initializers and object initializers, how the compiler translates each...
Two C# syntax features—the object initializer and the collection initializer—look nearly identical but behave differently. When you weigh c# collection initializer vs object initializer, the practical distinction comes down to what the compiler does with each form: one assigns properties, the other calls Add methods.
What Each Initializer Does
The object initializer and the collection initializer both appeared in C# 3.0, and both let you populate a new instance in a single expression. The similarity ends there. An object initializer assigns values to properties or fields on a freshly created object. A collection initializer adds elements to a collection by calling its Add method. The compiler translates each form into different calls, so the rules that govern them are different.
var person = new Person { FirstName = "Ada", LastName = "Lovelace" }; var numbers = new List<int> { 1, 2, 3 };
The first block creates a Person and sets two properties. The second creates a List<int> and adds three integers. Both look similar syntactically, but the generated code is not the same.
Object Initializer Syntax and Behavior
An object initializer works on any type that exposes accessible setters for the properties or fields you want to assign. It can be combined with a constructor that takes arguments:
var person = new Person("Ada") { LastName = "Lovelace" };
The constructor runs first, then each property setter executes in the order written. This ordering matters when the constructor performs validation that a property setter relies on, or when a setter has side effects.
You cannot use an object initializer to assign a read-only property unless the constructor already sets it. If the property has no setter, the compiler rejects the initializer. The same applies to fields that are readonly.
Collection Initializer Syntax and Behavior
A collection initializer requires the target type to implement IEnumerable and to expose a public Add method. The compiler emits a call to the constructor, then calls Add once per element in source order.
var dictionary = new Dictionary<string, int> { { "one", 1 }, { "two", 2 } };
For a Dictionary<TKey, TValue>, Add takes two parameters, so each element uses nested braces. The compiler passes both arguments to the same Add call.
Since C# 6, the Add method may also be an extension method. That means you can make a custom collection type support collection initializers without adding an instance method, as long as a compatible extension method is in scope.
Combining Both: Nested Initialization
The two features compose naturally. A collection initializer can contain object initializers:
var people = new List<Person> { new Person { FirstName = "Ada", LastName = "Lovelace" }, new Person { FirstName = "Grace", LastName = "Hopper" } };
Each element is a separate Person instance whose properties are set with an object initializer. The compiler emits a new call and property assignments for each element, then adds each instance to the list.
The reverse nesting also works. An object initializer can assign a collection property using a collection initializer:
var order = new Order { Id = 42, Items = new List<OrderItem> { new OrderItem { Name = "Book", Quantity = 1 } } };
This pattern is common when building test fixtures or configuration objects, because it keeps related data in one expression.
Common Mistakes and Edge Cases
A frequent mistake is assuming any type that implements IEnumerable supports a collection initializer. The type must also have a usable Add method. If Add is missing or not accessible, the code fails to compile even though the type is enumerable.
Another mistake involves read-only properties. An object initializer cannot set a property that has no setter. If you need to populate a read-only collection property, you must either pass the collection through the constructor or initialize it inside the type itself.
Arrays behave differently. An array initializer is a special case in the language:
int[] values = { 1, 2, 3 };
This does not call Add. It allocates the array and fills the elements directly. You cannot use a collection initializer syntax with an array type that has a custom Add method, because arrays do not expose one.
Order of execution is another consideration. When an Add method performs validation or has side effects, the elements are processed in the order they appear in the initializer. That ordering is guaranteed by the compiler, but it is easy to overlook when reading code.
Maintainability and Runtime Considerations
Both initializers are compile-time features. The generated IL is equivalent to writing the constructor call and the assignments or Add calls explicitly. There is no hidden runtime cost, no reflection, and no dynamic dispatch beyond what the equivalent statements would produce.
The readability benefit is real when an object has several properties or a collection has several elements. The syntax removes repetitive variable assignment code. It becomes less helpful when the initializer grows large. A deeply nested initializer with many levels can be harder to follow than a small factory method or a builder that names each step.
One maintainability concern is the Add method contract. If you later change a collection type to one without a compatible Add method, every collection initializer using that type stops compiling. The failure is clear, but the fix may require restructuring the call sites. Keeping Add methods stable on public collection types reduces that risk.
The choice between an object initializer and a collection initializer is rarely either/or. Most real code uses both, often nested. The practical question is whether a given type supports the syntax, and whether the resulting expression stays readable. When it does, the initializers produce concise, predictable code that behaves exactly like the explicit form.