diff --git a/docs/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md b/docs/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md index 50967a94e9548..f74873458a7c0 100644 --- a/docs/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md +++ b/docs/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md @@ -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) diff --git a/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md b/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md index a36d6f32e4644..6b86a1001474f 100644 --- a/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md +++ b/docs/csharp/programming-guide/classes-and-structs/object-and-collection-initializers.md @@ -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"::: @@ -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 diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs new file mode 100644 index 0000000000000..a9689e6974eb9 --- /dev/null +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs @@ -0,0 +1,79 @@ +namespace object_collection_initializers; + +// +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}"); + } +} +// + +// +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}"); + } +} +// \ No newline at end of file diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/Program.cs b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/Program.cs index c311d44d10821..8a6e4b5746960 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/Program.cs +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/Program.cs @@ -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(); } }