C# Object Initializer Syntax and Usage
c# object initializer: Learn C# object initializer syntax, how it maps to constructors and property setters, and when it improves readability versus other initializati...
c# object initializer requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.
Object initializers in C# let you set public properties or fields on an object at creation time, without writing a dedicated constructor for each combination of values. The syntax is simple: after the new expression, a block lists property assignments separated by commas. For example, given a class with public settable properties, you can initialize it like this:
var person = new Person { FirstName = "Ada", LastName = "Lovelace", Age = 36 };
This is equivalent to creating the object with the default constructor and then assigning each property individually. The C# compiler translates the object initializer into a sequence of property setter calls after the constructor runs, so it works with any type that exposes settable properties and has an accessible parameterless constructor (or a constructor you explicitly call). Understanding this mapping is key to using object initializers correctly, especially when properties are read-only or when the class has invariants that must be enforced.
How Object Initializers Map to Constructor and Property Calls
For the above example, the compiler generates code that effectively does:
var person = new Person(); person.FirstName = "Ada"; person.LastName = "Lovelace"; person.Age = 36;
The constructor runs first, then each property setter executes in the order they appear in the initializer. This order matters if the setters have side effects or validate dependencies between properties. Property assignments are executed sequentially, so in the initializer { A = ..., B = ... }, the setter for A runs before that for B. This is different from calling a constructor with named parameters, which all run inside the constructor body.
Because the generated code uses property setters, the properties must have accessible setters. A private setter or a get-only property prevents object initializer usage. The same applies to fields: you can initialize public fields, but that is less common in modern C# code.
When to Use an Object Initializer vs. a Constructor
Object initializers are most useful when you want to set a subset of available properties and the class has no constructor that takes exactly those parameters. For example, a configuration object might have twenty properties with sensible defaults, and different call sites need to override a few of them. Writing a constructor with twenty parameters would be unwieldy, and overloads would explode combinatorially.
var options = new ServiceOptions { TimeoutSeconds = 30, RetryCount = 3, Endpoint = "https://api.example.com" };
Here, the remaining properties fall back to the defaults defined in the ServiceOptions class. This is more readable than:
var options = new ServiceOptions(); options.TimeoutSeconds = 30; options.RetryCount = 3; options.Endpoint = "https://api.example.com";
Because the assignment lines are grouped with the creation, the intent is clearer. However, if the class requires certain values to be valid from the moment the object exists, you should prefer a constructor that enforces those requirements. Object initializers can temporarily leave the object in an incomplete state after construction; only after all setters run does the object reach its finalized state.
Nested and Collection Initializers
Object initializers also support nested objects and collections. You can initialize a property that itself is an object using the same syntax, and you can populate collections inline with collection initializers. Consider a type that has a List<string> property:
var team = new Team { Name = "Platform", Members = new List<string> { "Alice", "Bob", "Carol" } };
Here, Members is initialized by creating a new List<string> and then using the collection initializer syntax to add three items. This works because List<T> implements IEnumerable and has an Add method. If the property is read-only, you can still assign to it if it has a getter that returns a pre-initialized collection, but you cannot replace the entire collection through an object initializer. For instance:
public class Team { public List<string> Members { get; } = new List<string>(); } var team = new Team { Members = { "Alice", "Bob" } // This is not allowed; 'Members' is read-only. };
That example will not compile because the collection initializer would call the Add method on the property's getter result, but the compiler sees no accessible setter for Members and the assignment syntax fails. To populate a read-only collection property, you need to use a different approach, such as adding items after creation or providing an Add method on the containing class. This is a common gotcha.
Object Initializers with Non-Default Constructors
You can combine an object initializer with a non-default constructor by placing the constructor arguments in the new expression:
var account = new Account("checking", customerId: 123) { MinimumBalance = 100.00m, IsActive = true };
Here, the constructor sets the fields that are required for a valid account, and the initializer sets optional or additional properties. This is useful when certain parameters are mandatory, but others are optional and have properties with setters. Keep in mind that any validation enforced in the property setters runs after the constructor, so they must be safe to run in any order.
Limitations and Common Pitfalls
One limitation is that object initializers cannot be used with types that do not expose any settable properties or fields. For record types, the situation is different. Records support object initializers for positional properties, but the generated code uses the with expression for non-destructive mutation, which is separate. If you have an immutable record, you might prefer using a with expression to create a modified copy.
A common error is attempting to initialize a property that has a private setter, which results in a compile-time error. Another is misunderstanding the order of operations when a property setter validates or interacts with other properties. For example, if setter for Width confirms that Height is already set and greater than zero, initializing Height after Width may cause the validation to fail. In such cases, a factory method or constructor that sets values in a controlled sequence is safer.
Additionally, object initializers can make code harder to debug if a setter throws an exception because the stack trace points to the initializer line, but the actual failing setter may not be obvious. You can expand the line in the debugger to see which assignment failed, but experienced developers often prefer constructors for complex validation logic.
Object Initializers and Performance
From a runtime perspective, object initializers add negligible overhead. The compiler generates the same IL as consecutive assignment statements, so there is no reflection, no dynamic dispatch, and no hidden allocation beyond what the constructor and property setters already perform. The only performance consideration is that the constructor runs before the property setters, which is the same as manual assignment. If a property setter is computationally expensive, calling it through an initializer is no different from calling it directly. Comparing to a constructor that takes parameters, the object initializer may result in a temporary partially-initialized object if the setters have cross-dependencies, but that is a correctness issue, not a performance one.
For frequently created objects in a hot path, one subtle performance aspect is that the compiler cannot skip property setters based on default values; if you set a property to its default value, the setter still executes. This is rarely a real bottleneck but is worth noting if you are optimizing a tight loop.
Using Object Initializers with Anonymous Types
Anonymous types rely on object initializers as their core syntax. For example:
var product = new { Name = "Laptop", Price = 999.99m };
Here, the compiler infers the property names and types from the initializer. This is a primary use case for object initializers, and it requires that the values be expressions, not variables with names that differ. The compiler generates an immutable type with read-only properties, so you cannot add properties after creation. In LINQ queries, you often use anonymous types to project results into a shape without defining a class.
Object initializers for anonymous types are compiled into a type with get-only properties, so the order of assignments does not matter as long as the types are correct. The syntax is the same as for a normal class, but the resulting type is internal and has no public constructor.
Object Initializers vs. Init-Only Properties
C# 9 introduced init accessors, which allow properties to be set during object initialization but not after. This works with object initializers and gives a degree of immutability:
public class Point { public int X { get; init; } public int Y { get; init; } } var p = new Point { X = 1, Y = 2 }; // p.X = 3; // Compile-time error
The init accessor runs only during the object creation phase, including in object initializers and with expressions. This is a useful design pattern when you want to allow setting properties at construction but prevent later mutation. When using init, the generated code calls the init setter similarly to a normal setter, but the compiler enforces that it is only called from an initializer context. This is an important consideration for immutable data models where you want to avoid exposing settable properties.
Choosing the Right Initialization Strategy
When deciding between a constructor, an object initializer, or a with expression (for records), consider what invariants must hold. If an object is invalid without a particular property, that property should be a constructor parameter. If properties are optional and independent, object initializers make the call site readable. If you need an immutable copy with a few changes, a with expression on a record is the clearest.
A guideline is to use object initializers for DTOs, configuration options, and view models where the caller naturally wants to set a subset of properties. For domain objects that define behavior, prefer constructors that enforce invariants. For records, the positional constructor or with expressions are often the best fit.
Object initializers are a convenience feature that reduces boilerplate and improves readability, but they are not a replacement for proper constructors when the object's integrity depends on validation. Understanding how they map to underlying constructor and setter calls helps you avoid subtle bugs related to ordering and accessibility.
Handling Compound Initialization in Practice
A common pattern is initializing a list of objects with nested initializers:
var orders = new List<Order> { new Order { Id = 1, Total = 49.99m }, new Order { Id = 2, Total = 129.00m } };
This is a concise way to define a collection of objects with known data. It is widely used in tests and configuration. However, because the order of property assignment matters, if an Order had a setter that recalculates another property, the resulting object may differ from what you expect. Keep initializers simple and avoid setters with side effects when using this pattern.
For production code, consider building objects through a factory or builder when there are many optional parameters or complex validation. Object initializers are not a substitute for a builder pattern when you need to enforce a set of related conditions. They are best used for simple mapping scenarios.
In summary, object initializers are a concise and readable way to set public members during construction. They are compiled to standard property or field assignments, so they carry minimal runtime overhead. The main tradeoff is that validation is not centralized, and the object may be in an inconsistent state during initialization. Knowing when to use them, and when to rely on constructors or init-only properties, keeps your C# code maintainable and correct.