Skip to content

Add documentation for object initializer syntax without 'new' keyword #47036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ The next example shows the order of execution of constructor and member initiali

:::code language="csharp" source="snippets/object-collection-initializers/ObjectInitializersExecutionOrder.cs" id="ObjectInitializersExecutionOrder":::

## Object initializers without the `new` keyword

You can also use object initializer syntax without the `new` keyword to initialize properties of nested objects. This syntax is particularly useful with read-only properties:

:::code language="csharp" source="snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs" id="SnippetObjectInitializerWithoutNew":::

This approach modifies the existing instance of the nested object rather than creating a new one. For more details and examples, see [Object Initializers with class-typed properties](object-and-collection-initializers.md#object-initializers-with-class-typed-properties).

## See also

- [Object and Collection Initializers](object-and-collection-initializers.md)
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Object initializers let you assign values to any accessible fields or properties

The object initializers syntax allows you to create an instance, and after that it assigns the newly created object, with its assigned properties, to the variable in the assignment.

Starting with nested object properties, you can use object initializer syntax without the `new` keyword. This syntax, `Property = { ... }`, allows you to initialize members of existing nested objects, which is particularly useful with read-only properties. For more details, see [Object Initializers with class-typed properties](#object-initializers-with-class-typed-properties).

Object initializers can set indexers, in addition to assigning fields and properties. Consider this basic `Matrix` class:

:::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="MatrixDeclaration":::
Expand Down Expand Up @@ -124,11 +126,30 @@ Required init-only properties support immutable structures while allowing natura

## Object Initializers with class-typed properties

It's crucial to consider the implications for class-typed properties when initializing an object:
When initializing objects with class-typed properties, you can use two different syntaxes:

1. **Object initializer without `new` keyword**: `Property = { ... }`
2. **Object initializer with `new` keyword**: `Property = new() { ... }`

These syntaxes behave differently. The following example demonstrates both approaches:

:::code language="csharp" source="./snippets/object-collection-initializers/HowToClassTypedInitializer.cs" id="HowToClassTypedInitializer":::

The following example shows how, for ClassB, the initialization process involves updating specific values while retaining others from the original instance. The Initializer reuses current instance: ClassB's values are: `100003` (new value we assign here), `true` (kept from EmbeddedClassTypeA's initialization), `BBBabc` (unchanged default from EmbeddedClassTypeB).
### Key differences

- **Without `new` keyword** (`ClassB = { BI = 100003 }`): This syntax modifies the existing instance of the property that was created during object construction. It calls member initializers on the existing object.

- **With `new` keyword** (`ClassB = new() { BI = 100003 }`): This syntax creates a completely new instance and assigns it to the property, replacing any existing instance.

The initializer without `new` reuses the current instance. In the example above, ClassB's values are: `100003` (new value assigned), `true` (kept from EmbeddedClassTypeA's initialization), `BBBabc` (unchanged default from EmbeddedClassTypeB).

### Object initializers without `new` for read-only properties

The syntax without `new` is particularly useful with read-only properties, where you can't assign a new instance but can still initialize the existing instance's members:

:::code language="csharp" source="./snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs" id="ReadOnlyPropertyExample":::

This approach allows you to initialize nested objects even when the containing property doesn't have a setter.

## Collection initializers

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
namespace object_collection_initializers;

// <SnippetObjectInitializerWithoutNew>
public class ObjectInitializerWithoutNew
{
public class Address
{
public string Street { get; set; } = "";
public string City { get; set; } = "";
public string State { get; set; } = "";
}

public class Person
{
public string Name { get; set; } = "";
public Address HomeAddress { get; set; } = new(); // Property with setter
}

public static void Examples()
{
// Example 1: Using object initializer WITHOUT 'new' keyword
// This modifies the existing Address instance created in the constructor
var person1 = new Person
{
Name = "Alice",
HomeAddress = { Street = "123 Main St", City = "Anytown", State = "CA" }
};

// Example 2: Using object initializer WITH 'new' keyword
// This creates a completely new Address instance
var person2 = new Person
{
Name = "Bob",
HomeAddress = new Address { Street = "456 Oak Ave", City = "Somewhere", State = "NY" }
};

// Both approaches work, but they behave differently:
// - person1.HomeAddress is the same instance that was created in Person's constructor
// - person2.HomeAddress is a new instance, replacing the one from the constructor

Console.WriteLine($"Person 1: {person1.Name} at {person1.HomeAddress.Street}, {person1.HomeAddress.City}, {person1.HomeAddress.State}");
Console.WriteLine($"Person 2: {person2.Name} at {person2.HomeAddress.Street}, {person2.HomeAddress.City}, {person2.HomeAddress.State}");
}
}
// </SnippetObjectInitializerWithoutNew>

// <SnippetReadOnlyPropertyExample>
public class ReadOnlyPropertyExample
{
public class Settings
{
public string Theme { get; set; } = "Light";
public int FontSize { get; set; } = 12;
}

public class Application
{
public string Name { get; set; } = "";
// This property is read-only - it can only be set during construction
public Settings AppSettings { get; } = new();
}

public static void Example()
{
// You can still initialize the nested object's properties
// even though AppSettings property has no setter
var app = new Application
{
Name = "MyApp",
AppSettings = { Theme = "Dark", FontSize = 14 }
};

// This would cause a compile error because AppSettings has no setter:
// app.AppSettings = new Settings { Theme = "Dark", FontSize = 14 };

Console.WriteLine($"App: {app.Name}, Theme: {app.AppSettings.Theme}, Font Size: {app.AppSettings.FontSize}");
}
}
// </SnippetReadOnlyPropertyExample>
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ static void Main(string[] args)
HowToIndexInitializer.Main();
HowToDictionaryInitializer.Main();
ObjectInitializersExecutionOrder.Main();

Console.WriteLine("\n--- Object Initializer Without New Examples ---");
ObjectInitializerWithoutNew.Examples();
ReadOnlyPropertyExample.Example();
}
}