C# object & collection initializers
last modified October 17, 2026
In this article we show how to initialize objects and collections with initializers in C#.
With object initializers, we can assign values to any accessible fields or properties of an object at creation time without having to invoke a constructor followed by lines of assignment statements. With collection initializers, we can fill a collection with elements when it is created.
C# object initializer
In the following example, we use an object initializer.
var u1 = new User { FirstName = "John", LastName = "Doe", Occupation = "gardener" };
var u2 = new User { LastName = "Roe", Occupation = "driver", FirstName = "Roger" };
Console.WriteLine(u1);
Console.WriteLine(u2);
class User
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Occupation { get; init; } = "unknown";
public override string ToString()
{
return $"User {FirstName} {LastName} {Occupation}";
}
}
We create two users utilizing the object initializer syntax.
var u1 = new User { FirstName = "John", LastName = "Doe", Occupation = "gardener" };
The member assignments are placed inside a pair of {} brackets
and are separated with a comma character. The object is first created with its
parameterless constructor; then the assignments are executed.
var u2 = new User { LastName = "Roe", Occupation = "driver", FirstName = "Roger" };
The order of the members is not relevant.
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Occupation { get; init; } = "unknown";
The init accessor allows a property to be set only during object
initialization; afterwards the object is immutable. The required
modifier forces the caller to set the property in the object initializer;
leaving it out is a compile-time error. The Occupation property is
optional and has a default value.
$ dotnet run User John Doe gardener User Roger Roe driver
If we omit a required member, for instance with
new User { FirstName = "John" }, the compiler reports the
CS9035 error. See the C# required
modifier and C# init members articles
for more details.
C# object initializer with constructor
An object initializer can follow a constructor call with arguments. It works with classes as well as with records.
var c = new Car("Skoda") { Price = 23000, Color = "red" };
Console.WriteLine(c);
var p = new Person("Jane", "Doe") { Email = "jane@example.com" };
Console.WriteLine(p);
class Car(string name)
{
public string Name { get; } = name;
public int Price { get; set; }
public string? Color { get; set; }
public override string ToString() => $"{Name}: {Price}, {Color}";
}
record Person(string FirstName, string LastName)
{
public string? Email { get; init; }
}
The mandatory data are passed to the constructor, the optional ones are set in the initializer.
var p = new Person("Jane", "Doe") { Email = "jane@example.com" };
The Person record has two positional properties and an additional
Email property, which we set with the object initializer.
$ dotnet run
Skoda: 23000, red
Person { FirstName = Jane, LastName = Doe, Email = jane@example.com }
C# nested object initializers
Object initializers can be nested. A nested initializer can either assign a new object to a property or set the members of an object that the property already holds.
var order = new Order
{
Id = 1,
Customer = new Customer { Name = "John Doe", City = "London" },
Address = { Street = "Baker Street 221B", Zip = "NW1 6XE" },
Items = { "pen", "notebook" }
};
Console.WriteLine($"{order.Id} {order.Customer.Name} from {order.Customer.City}");
Console.WriteLine($"{order.Address.Street}, {order.Address.Zip}");
Console.WriteLine(string.Join(", ", order.Items));
class Order
{
public int Id { get; set; }
public Customer? Customer { get; set; }
public Address Address { get; } = new();
public List<string> Items { get; } = [];
}
class Customer
{
public string? Name { get; set; }
public string? City { get; set; }
}
class Address
{
public string? Street { get; set; }
public string? Zip { get; set; }
}
We create an order with a customer, an address, and a list of items.
Customer = new Customer { Name = "John Doe", City = "London" },
A new Customer object is created and assigned to the property.
Address = { Street = "Baker Street 221B", Zip = "NW1 6XE" },
Items = { "pen", "notebook" }
Without the new keyword, the initializer sets the members of the
existing Address object and adds elements to the existing list.
This works even with read-only properties, because the properties themselves
are not assigned.
$ dotnet run 1 John Doe from London Baker Street 221B, NW1 6XE pen, notebook
C# object initializer with anonymous type
We can create anonymous types with object initializers; this is useful in LINQ expressions.
var u1 = new { FirstName = "John", LastName = "Doe", Occupation = "gardener" };
var u2 = new { FirstName = "Roger", LastName = "Roe", Occupation = "driver" };
Console.WriteLine(u1);
Console.WriteLine(u2);
Console.WriteLine(u1.FirstName);
The example creates two users. The compiler generates a type with read-only
properties for them; the type has no name that we could use in our code, so
the variables must be declared with var.
$ dotnet run
{ FirstName = John, LastName = Doe, Occupation = gardener }
{ FirstName = Roger, LastName = Roe, Occupation = driver }
John
In the next example, we use an anonymous type in a LINQ query expression.
List<User> users =
[
new(1, "John", "Doe", "London", "gardener", 860),
new(2, "Lenny", "Ferguson", "New York", "programmer", 980),
new(3, "Andrew", "Ramos", "Boston", "shop keeper", 600),
new(4, "Peter", "Stastny", "Prague", "retired", 500),
new(5, "Anna", "Fabry", "Bratislava", "accountant", 780),
];
var data = from user in users
select new { user.FirstName, user.LastName, user.Salary };
foreach (var u in data)
{
Console.WriteLine(u);
}
record User(int Id, string FirstName, string LastName, string City,
string Occupation, decimal Salary);
We have a list of users where each user has six properties. In the LINQ query
expression, we generate a sequence of anonymous objects, where each item has
only a subset of the properties. When a property is initialized from another
property, its name is inferred; user.FirstName creates a
FirstName property.
$ dotnet run
{ FirstName = John, LastName = Doe, Salary = 860 }
{ FirstName = Lenny, LastName = Ferguson, Salary = 980 }
{ FirstName = Andrew, LastName = Ramos, Salary = 600 }
{ FirstName = Peter, LastName = Stastny, Salary = 500 }
{ FirstName = Anna, LastName = Fabry, Salary = 780 }
C# list initializer
In the following example, we use a list initializer.
var vals = new List<int> { 1, -2, -1, 0, 2, 4, 3, -5 };
var positive = vals.Where(val => val > 0);
Console.WriteLine(string.Join(" ", positive));
var users = new List<User>
{
new() { FirstName = "John", LastName = "Doe", Occupation = "gardener" },
new() { FirstName = "Roger", LastName = "Roe", Occupation = "driver" },
};
foreach (var user in users)
{
Console.WriteLine(user);
}
class User
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
public required string Occupation { get; init; }
public override string ToString() => $"User {FirstName} {LastName} {Occupation}";
}
We create a list of integers and a list of users.
var vals = new List<int> { 1, -2, -1, 0, 2, 4, 3, -5 };
The list is initialized in one go; all the elements are specified within the
pair of {} brackets.
var users = new List<User>
{
new() { FirstName = "John", LastName = "Doe", Occupation = "gardener" },
new() { FirstName = "Roger", LastName = "Roe", Occupation = "driver" },
};
The elements of the list are created with object initializers. With the
target-typed new() expression, we do not need to repeat the type
name.
$ dotnet run 1 2 4 3 User John Doe gardener User Roger Roe driver
C# collection initializer and the Add method
A collection initializer is a shorthand for calling the Add
method for each element. It works with any type that implements
IEnumerable and has an accessible Add method.
using System.Collections;
var bag = new Bag { "apple", "pear", "plum" };
Console.WriteLine(bag);
class Bag : IEnumerable<string>
{
private readonly List<string> items = [];
public void Add(string item)
{
Console.WriteLine($"adding {item}");
items.Add(item);
}
public IEnumerator<string> GetEnumerator() => items.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public override string ToString() => string.Join(", ", items);
}
Our custom Bag collection can be filled with a collection
initializer. The output shows that the compiler calls the Add
method for each element.
$ dotnet run adding apple adding pear adding plum apple, pear, plum
C# collection expressions
Since C# 12, collections can also be created with collection
expressions. They use square brackets and work with arrays, lists, spans,
and other collection types. The .. spread element inserts all the
elements of another collection.
int[] vals = [1, 2, 3];
List<int> more = [4, 5, 6];
HashSet<string> words = ["sky", "blue", "sky"];
Span<char> chars = ['a', 'b', 'c'];
int[] all = [0, .. vals, .. more, 7];
Console.WriteLine(string.Join(" ", all));
Console.WriteLine(string.Join(" ", words));
Console.WriteLine(chars.Length);
List<int> empty = [];
Console.WriteLine(empty.Count);
The same syntax creates collections of different types; the type is given by the target. The hash set keeps only unique values.
$ dotnet run 0 1 2 3 4 5 6 7 sky blue 3 0
See the C# collection expressions article for more details.
C# Dictionary initializers
Dictionary initializers can be specified in two ways.
var domains = new Dictionary<string, string>
{
{ "sk", "Slovakia" },
{ "ru", "Russia" },
{ "de", "Germany" },
{ "no", "Norway" }
};
Console.WriteLine(domains["sk"]);
var days = new Dictionary<string, string>
{
["mo"] = "Monday",
["tu"] = "Tuesday",
["we"] = "Wednesday",
["th"] = "Thursday",
["fr"] = "Friday",
["sa"] = "Saturday",
["su"] = "Sunday"
};
Console.WriteLine(days["fr"]);
The example creates two dictionaries.
var domains = new Dictionary<string, string>
{
{ "sk", "Slovakia" },
{ "ru", "Russia" },
{ "de", "Germany" },
{ "no", "Norway" }
};
A new dictionary is created. Between the angle brackets <>,
we specify the data types of the keys and values. New pairs of key/value
elements are written inside nested {} brackets; each pair is
separated by a comma character. This is a collection initializer; each pair is
passed to the Add method.
Console.WriteLine(domains["sk"]);
To get a value, we specify the dictionary name followed by square
[] brackets. Between the brackets, we specify the key.
var days = new Dictionary<string, string>
{
["mo"] = "Monday",
["tu"] = "Tuesday",
...
};
This is an alternative syntax, called an index initializer; the values are assigned to keys using the indexer.
$ dotnet run Slovakia Friday
The two forms differ when a key is repeated. The Add method throws
an ArgumentException for a duplicate key, while the indexer
silently overwrites the previous value.
var d1 = new Dictionary<string, int>
{
["a"] = 1,
["a"] = 2
};
Console.WriteLine(d1["a"]);
try
{
var d2 = new Dictionary<string, int>
{
{ "a", 1 },
{ "a", 2 }
};
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message);
}
The first dictionary contains the last value; creating the second one fails.
$ dotnet run 2 An item with the same key has already been added. Key: a
Source
Object and Collection Initializers
In this article we have worked with object & collection initializers in C#.
Author
List all C# tutorials.