C# Object Initialization Explained
c# object initialization: Learn how C# object initialization works: object initializers, constructors, init-only properties, and required members, with practical examp...
C# object initialization is the process of creating and configuring an object instance in a single expression. The most direct form uses a constructor and assignment statements, but C# offers object initializers, collection initializers, and newer features like init accessors and required members that change how objects should be constructed. Choosing the right initialization approach affects code readability, immutability, and whether an object can be created in a partially configured state.
The Basic Syntax of Object Initializers
An object initializer lets you set public properties or fields at the moment of construction without explicitly calling a constructor with matching parameters. The syntax wraps property assignments in braces after the constructor call.
var person = new Person { FirstName = "Ada", LastName = "Lovelace" };
This code compiles to a default constructor call followed by assignments to the FirstName and LastName properties. The object is not fully constructed until the constructor completes, and the property setters run in the order you write them. That ordering matters if one setter depends on another, although such dependencies are usually a design smell.
Object initializers work with any accessible constructor. You can combine constructor parameters with initializer assignments:
var employee = new Employee("E123") { Department = "Engineering", StartDate = DateTime.Today };
The constructor establishes the required identity (the employee ID), and the initializer sets optional or context-dependent values.
Object Initializers vs. Constructors: A Decision Framework
Some developers treat object initializers as a replacement for constructors, but the two serve different purposes. A constructor is the right place to enforce invariants that must hold regardless of how the object is used. For example, a BankAccount should not exist without an account number and an initial balance. Those should be constructor parameters.
Object initializers are better for optional properties where a default value is acceptable but callers may want to override it. Consider a Report class with properties like Title, Author, and IncludeFooter. A constructor with ten parameters is hard to read at the call site, especially when most parameters are optional. Object initializers make the assignment explicit:
var report = new Report { Title = "Q3 Earnings", Author = "Finance" };
The tradeoff is that object initializers give no compile-time guarantee that required properties are set. Nothing stops you from forgetting Title. If a property must always be present, put it in the constructor or use the required modifier described later.
How Object Initialization Works Under the Hood
The compiler transforms an object initializer into a constructor call followed by assignment statements. For the Person example, the compiled code is roughly equivalent to:
Person _temp = new Person(); _temp.FirstName = "Ada"; _temp.LastName = "Lovelace"; var person = _temp;
This temporary variable matters when the initializer runs in a context that could observe a partially initialized object, such as inside a constructor that calls a virtual method. In most code the distinction is irrelevant, but it explains why an object initializer cannot be used with a type that has only private setters or with an interface that exposes no setters.
Collection Initializers for Lists and Dictionaries
Collection initializers follow the same idea but apply to types that implement IEnumerable and have an accessible Add method. They let you populate a list or dictionary inline:
var numbers = new List<int> { 1, 2, 3 }; var lookup = new Dictionary<string, int> { ["one"] = 1, ["two"] = 2 };
For a dictionary, the indexer syntax in the initializer calls the Add method with (key, value) pairs. The order of insertion is preserved for List<T>, but for Dictionary<TKey, TValue> iteration order is not guaranteed, so you should not rely on it.
init-Only Properties and Immutable Objects
C# 9 introduced init accessors, which allow a property to be set only during object initialization. After that, the property is effectively read-only.
public class Person { public string FirstName { get; init; } public string LastName { get; init; } }
With this definition, you can still use an object initializer:
var person = new Person { FirstName = "Ada", LastName = "Lovelace" };
But after construction, person.FirstName = "Grace"; produces a compile-time error. This is valuable for immutable objects: you get the conciseness of an object initializer and the safety of immutability. However, init properties can only be set in the constructor, in an object initializer, or in a with expression. If you need to modify the object later, you must use a regular setter or create a new instance.
required Members: Enforcing Non-Nullable Initialization
C# 11 introduced required members, which force callers to set a property or field during object initialization. When a property is marked required, you cannot create the object without assigning it in an object initializer or through a constructor that sets it.
public class Product { public required string Name { get; init; } public decimal Price { get; init; } } var product = new Product { Name = "Laptop", Price = 1299.99m };
If you omit Name, the compiler reports an error. This combines the clarity of object initializers with the safety of constructor validation for required data. One important constraint is that required works well with object initializers, but it does not offer runtime validation. If you need a custom check (for example, rejecting an empty string), you still need a constructor or a validation method.
Runtime Cost and Memory Behavior
Object initializers are a compile-time feature. They do not introduce reflection, dynamic dispatch, or any additional runtime allocation. The generated IL is essentially a constructor call followed by property setters, which is the same as writing those assignments manually. Therefore, there is no performance penalty compared to explicit assignment.
The only subtle cost is that the property setter itself runs, which could contain logic. If a setter is expensive or has side effects, an object initializer will trigger it just like any other assignment. That is a design concern, not an initialization-specific one.
A more meaningful performance consideration is the use of init and required in conjunction with the with expression in records:
var updatedProduct = product with { Price = 1199.99m };
This creates a new instance and copies all fields, which is a cheap operation for small objects. But for large objects, copying can become a measurable cost. In such cases, weigh the immutability benefit against the allocation overhead.
Practical Tips and Common Pitfalls
One common pitfall is using object initializers with types that have a non-default constructor but also expose setters that must be called in a specific order. Because the compiler calls setters in the order written, you can accidentally break an invariant. Prefer constructors for any initialization that must happen in a strict sequence.
Another mistake is assuming object initializers work with anonymous types. Anonymous types use a different syntax that looks similar but is not a general object initializer:
var anon = new { Name = "Ada", Year = 1843 };
Anonymous types are read-only and compile to internal classes, which is a distinct feature, not an object initializer.
When designing a class that will be used with object initializers, keep properties simple. Avoid setters that depend on other properties being set first, because the caller controls the order. If you must have such dependencies, expose a constructor that takes the required values in the correct order.
For APIs that return data transfer objects, consider whether init plus object initializers is more maintainable than a large constructor. The answer depends on the number of properties and whether the type is meant to be immutable. A DTO with ten read-only properties benefits from init because callers can specify only the fields they care about,