Skip to content

Commit fc5e658

Browse files
CopilotBillWagnerIEvangelist
authored
Add documentation for object initializer syntax without 'new' keyword (#47036)
* Initial plan * Add documentation for object initializer syntax without 'new' keyword Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> * Replace inline code example with snippet reference for object initializer without 'new' keyword Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> * Update object initializer examples to use target-typed new expressions Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> Co-authored-by: IEvangelist <7679720+IEvangelist@users.noreply.github.com>
1 parent a910d3f commit fc5e658

File tree

4 files changed

+114
-2
lines changed

4 files changed

+114
-2
lines changed

docs/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@ The next example shows the order of execution of constructor and member initiali
3030

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

33+
## Object initializers without the `new` keyword
34+
35+
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:
36+
37+
:::code language="csharp" source="snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs" id="SnippetObjectInitializerWithoutNew":::
38+
39+
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).
40+
3341
## See also
3442

3543
- [Object and Collection Initializers](object-and-collection-initializers.md)

docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Object initializers let you assign values to any accessible fields or properties
1919

2020
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.
2121

22+
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).
23+
2224
Object initializers can set indexers, in addition to assigning fields and properties. Consider this basic `Matrix` class:
2325

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

125127
## Object Initializers with class-typed properties
126128

127-
It's crucial to consider the implications for class-typed properties when initializing an object:
129+
When initializing objects with class-typed properties, you can use two different syntaxes:
130+
131+
1. **Object initializer without `new` keyword**: `Property = { ... }`
132+
2. **Object initializer with `new` keyword**: `Property = new() { ... }`
133+
134+
These syntaxes behave differently. The following example demonstrates both approaches:
128135

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

131-
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).
138+
### Key differences
139+
140+
- **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.
141+
142+
- **With `new` keyword** (`ClassB = new() { BI = 100003 }`): This syntax creates a completely new instance and assigns it to the property, replacing any existing instance.
143+
144+
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).
145+
146+
### Object initializers without `new` for read-only properties
147+
148+
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:
149+
150+
:::code language="csharp" source="./snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs" id="ReadOnlyPropertyExample":::
151+
152+
This approach allows you to initialize nested objects even when the containing property doesn't have a setter.
132153

133154
## Collection initializers
134155

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
namespace object_collection_initializers;
2+
3+
// <SnippetObjectInitializerWithoutNew>
4+
public class ObjectInitializerWithoutNew
5+
{
6+
public class Address
7+
{
8+
public string Street { get; set; } = "";
9+
public string City { get; set; } = "";
10+
public string State { get; set; } = "";
11+
}
12+
13+
public class Person
14+
{
15+
public string Name { get; set; } = "";
16+
public Address HomeAddress { get; set; } = new(); // Property with setter
17+
}
18+
19+
public static void Examples()
20+
{
21+
// Example 1: Using object initializer WITHOUT 'new' keyword
22+
// This modifies the existing Address instance created in the constructor
23+
var person1 = new Person
24+
{
25+
Name = "Alice",
26+
HomeAddress = { Street = "123 Main St", City = "Anytown", State = "CA" }
27+
};
28+
29+
// Example 2: Using object initializer WITH 'new' keyword
30+
// This creates a completely new Address instance
31+
var person2 = new Person
32+
{
33+
Name = "Bob",
34+
HomeAddress = new Address { Street = "456 Oak Ave", City = "Somewhere", State = "NY" }
35+
};
36+
37+
// Both approaches work, but they behave differently:
38+
// - person1.HomeAddress is the same instance that was created in Person's constructor
39+
// - person2.HomeAddress is a new instance, replacing the one from the constructor
40+
41+
Console.WriteLine($"Person 1: {person1.Name} at {person1.HomeAddress.Street}, {person1.HomeAddress.City}, {person1.HomeAddress.State}");
42+
Console.WriteLine($"Person 2: {person2.Name} at {person2.HomeAddress.Street}, {person2.HomeAddress.City}, {person2.HomeAddress.State}");
43+
}
44+
}
45+
// </SnippetObjectInitializerWithoutNew>
46+
47+
// <SnippetReadOnlyPropertyExample>
48+
public class ReadOnlyPropertyExample
49+
{
50+
public class Settings
51+
{
52+
public string Theme { get; set; } = "Light";
53+
public int FontSize { get; set; } = 12;
54+
}
55+
56+
public class Application
57+
{
58+
public string Name { get; set; } = "";
59+
// This property is read-only - it can only be set during construction
60+
public Settings AppSettings { get; } = new();
61+
}
62+
63+
public static void Example()
64+
{
65+
// You can still initialize the nested object's properties
66+
// even though AppSettings property has no setter
67+
var app = new Application
68+
{
69+
Name = "MyApp",
70+
AppSettings = { Theme = "Dark", FontSize = 14 }
71+
};
72+
73+
// This would cause a compile error because AppSettings has no setter:
74+
// app.AppSettings = new Settings { Theme = "Dark", FontSize = 14 };
75+
76+
Console.WriteLine($"App: {app.Name}, Theme: {app.AppSettings.Theme}, Font Size: {app.AppSettings.FontSize}");
77+
}
78+
}
79+
// </SnippetReadOnlyPropertyExample>

docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/Program.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ static void Main(string[] args)
99
HowToIndexInitializer.Main();
1010
HowToDictionaryInitializer.Main();
1111
ObjectInitializersExecutionOrder.Main();
12+
13+
Console.WriteLine("\n--- Object Initializer Without New Examples ---");
14+
ObjectInitializerWithoutNew.Examples();
15+
ReadOnlyPropertyExample.Example();
1216
}
1317
}

0 commit comments

Comments
 (0)