diff --git a/.github/workflows/snippets5000.yml b/.github/workflows/snippets5000.yml index 79f7a32212678..0680738c831bd 100644 --- a/.github/workflows/snippets5000.yml +++ b/.github/workflows/snippets5000.yml @@ -14,7 +14,7 @@ on: default: 'Manual run' env: - DOTNET_VERSION: '10.0.*' + DOTNET_VERSION: '11.0.*' DOTNET_QUALITY: 'preview' DOTNET_DO_INSTALL: 'true' # To install a version of .NET not provided by the runner, set to true EnableNuGetPackageRestore: 'True' diff --git a/.openpublishing.redirection.csharp.json b/.openpublishing.redirection.csharp.json index 4706854645830..0092422dc85fe 100644 --- a/.openpublishing.redirection.csharp.json +++ b/.openpublishing.redirection.csharp.json @@ -5014,7 +5014,7 @@ }, { "source_path_from_root": "/docs/csharp/whats-new.md", - "redirect_url": "/dotnet/csharp/whats-new/csharp-14", + "redirect_url": "/dotnet/csharp/whats-new/csharp-15", "redirect_document_id": true }, { @@ -5055,7 +5055,7 @@ }, { "source_path_from_root": "/docs/csharp/whats-new/index.md", - "redirect_url": "/dotnet/csharp/whats-new/csharp-14", + "redirect_url": "/dotnet/csharp/whats-new/csharp-15", "ms.custom": "updateeachrelease" }, { diff --git a/docfx.json b/docfx.json index 32baf4eabedc3..30fbfafc3f690 100644 --- a/docfx.json +++ b/docfx.json @@ -74,6 +74,7 @@ "Compiler Breaking Changes - DotNet 8.md", "Compiler Breaking Changes - DotNet 9.md", "Compiler Breaking Changes - DotNet 10.md", + "Compiler Breaking Changes - DotNet 11.md", "Deviations from Standard.md" ], "src": "_roslyn/docs/compilers/CSharp", @@ -513,11 +514,12 @@ "_csharplang/proposals/csharp-12.0/*.md": "08/15/2023", "_csharplang/proposals/csharp-13.0/*.md": "10/31/2024", "_csharplang/proposals/csharp-14.0/*.md": "08/06/2025", - "_csharplang/proposals/*.md": "08/06/2025", + "_csharplang/proposals/*.md": "02/04/2025", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "11/08/2023", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "11/09/2024", - "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md": "01/09/2024", + "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md": "11/09/2024", + "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md": "01/09/2025", "_roslyn/docs/compilers/CSharp/Deviations from Standard.md": "04/24/2025", "_vblang/spec/*.md": "07/21/2017" }, @@ -693,10 +695,12 @@ "_csharplang/proposals/csharp-14.0/ignored-directives.md": "Ignored preprocessor directives", "_csharplang/proposals/csharp-14.0/extension-operators.md": "Extension operators", "_csharplang/proposals/csharp-14.0/optional-and-named-parameters-in-expression-trees.md": "Optional and named parameters in expression trees", + "_csharplang/proposals/collection-expression-arguments.md": "Collection expression arguments", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md": "C# compiler breaking changes since C# 13", + "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md": "C# compiler breaking changes since C# 14", "_roslyn/docs/compilers/CSharp/Deviations from Standard.md": "Deviations from the C# standard", "_vblang/spec/introduction.md": "Introduction", "_vblang/spec/lexical-grammar.md": "Lexical grammar", @@ -819,10 +823,12 @@ "_csharplang/proposals/csharp-14.0/ignored-directives.md": "This proposal allows a source file to include ignored directives. In most cases, ignored directives are used for file-based apps, for example `#!`", "_csharplang/proposals/csharp-14.0/extension-operators.md": "This proposal extends the proposal for extensions to include *extension operators*, where an operator can be an extension member.", "_csharplang/proposals/csharp-14.0/optional-and-named-parameters-in-expression-trees.md": "This proposal allows an expression tree to include named and optional parameters. This enables expression trees to be more flexible in how they are constructed.", + "_csharplang/proposals/collection-expression-arguments.md": "This proposal introduces collection expression arguments.", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 10.md": "Learn about any breaking changes since the initial release of C# 13 and included in C# 14", + "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 11.md": "Learn about any breaking changes since the initial release of C# 14 and included in C# 15", "_roslyn/docs/compilers/CSharp/Deviations from Standard.md": "Learn about any deviations from the C# standard", "_vblang/spec/introduction.md": "This chapter provides an overview of the Visual Basic language and its design goals.", "_vblang/spec/lexical-grammar.md": "This chapter defines the lexical grammar for Visual Basic.", diff --git a/docs/csharp/language-reference/includes/default-langversion-table.md b/docs/csharp/language-reference/includes/default-langversion-table.md index f9185ba55aa01..268b9da577954 100644 --- a/docs/csharp/language-reference/includes/default-langversion-table.md +++ b/docs/csharp/language-reference/includes/default-langversion-table.md @@ -4,6 +4,7 @@ ms.custom: "updateeachrelease" | Target | Version | C# language version default | |------------------|---------|-----------------------------| +| .NET | 11.x | C# 15 | | .NET | 10.x | C# 14 | | .NET | 9.x | C# 13 | | .NET | 8.x | C# 12 | diff --git a/docs/csharp/language-reference/includes/langversion-table.md b/docs/csharp/language-reference/includes/langversion-table.md index d7d81b3b80f23..946050c676652 100644 --- a/docs/csharp/language-reference/includes/langversion-table.md +++ b/docs/csharp/language-reference/includes/langversion-table.md @@ -7,6 +7,7 @@ ms.custom: "updateeachrelease" | `preview` | The compiler accepts all valid language syntax from the latest preview version. | | `latest` | The compiler accepts syntax from the latest released version of the compiler (including minor version). | | `latestMajor`
or `default` | The compiler accepts syntax from the latest released major version of the compiler. | +| `15.0` | The compiler accepts only syntax that is included in C# 15 or lower. | | `14.0` | The compiler accepts only syntax that is included in C# 14 or lower. | | `13.0` | The compiler accepts only syntax that is included in C# 13 or lower. | | `12.0` | The compiler accepts only syntax that is included in C# 12 or lower. | diff --git a/docs/csharp/language-reference/keywords/index.md b/docs/csharp/language-reference/keywords/index.md index 41542291e91ac..2e4f273d6634e 100644 --- a/docs/csharp/language-reference/keywords/index.md +++ b/docs/csharp/language-reference/keywords/index.md @@ -166,7 +166,7 @@ A contextual keyword provides a specific meaning in the code, but it isn't a res [`when` (filter condition)](when.md) [`where` (generic type constraint)](where-generic-type-constraint.md) [`where` (query clause)](where-clause.md) - [`with`](../operators/with-expression.md) + [`with`](with.md) [`yield`](../statements/yield.md) :::column-end::: :::row-end::: diff --git a/docs/csharp/language-reference/keywords/with.md b/docs/csharp/language-reference/keywords/with.md new file mode 100644 index 0000000000000..86aed042beb3c --- /dev/null +++ b/docs/csharp/language-reference/keywords/with.md @@ -0,0 +1,21 @@ +--- +description: "with - C# Reference" +title: "`with` contextual keyword" +ms.date: 02/06/2026 +f1_keywords: + - "with" + - "with_CSharpKeyword" +helpviewer_keywords: + - "with keyword [C#]" +--- + +# `with` (C# reference) + +Use the `with` keyword in the following contexts: + +- In a [`with` expression](../operators/with-expression.md) to create a copy and apply modifications to one or more properties. +- In a [collection expression](../operators/collection-expressions.md) to specify parameters to the constructor for the initialized collection. + +## See also + +- [C# Keywords](index.md) diff --git a/docs/csharp/language-reference/language-versioning.md b/docs/csharp/language-reference/language-versioning.md index ab9665c33cb40..16847f0bbeec1 100644 --- a/docs/csharp/language-reference/language-versioning.md +++ b/docs/csharp/language-reference/language-versioning.md @@ -2,7 +2,7 @@ title: Language versioning description: Learn about how the C# language version is determined based on your project and the reasons behind that choice. ms.custom: "updateeachrelease" -ms.date: 01/16/2026 +ms.date: 02/04/2026 --- # C# language versioning @@ -11,7 +11,7 @@ The latest C# compiler determines a default language version based on your proje [!INCLUDE[csharp-version-note](./includes/initial-version.md)] -[C# 14](../whats-new/csharp-14.md) is supported only on .NET 10 and newer versions. [C# 13](../whats-new/csharp-13.md) is supported only on .NET 9 and newer versions. [C# 12](../whats-new/csharp-12.md) is supported only on .NET 8 and newer versions. Using a C# language version newer than the version associated with your target TFM is unsupported. +[C# 15](../whats-new/csharp-15.md) is supported only on .NET 11 and newer versions. [C# 14](../whats-new/csharp-14.md) is supported only on .NET 10 and newer versions. [C# 13](../whats-new/csharp-13.md) is supported only on .NET 9 and newer versions. Using a C# language version newer than the version associated with your target TFM is unsupported. For details on which .NET versions are supported by versions of Visual Studio, see the [Visual Studio platform compatibility](/visualstudio/releases/2026/compatibility#-visual-studio-support-for-net-development) page. diff --git a/docs/csharp/language-reference/operators/collection-expressions.md b/docs/csharp/language-reference/operators/collection-expressions.md index 5024f70e471f8..f5fb5ebb480ae 100644 --- a/docs/csharp/language-reference/operators/collection-expressions.md +++ b/docs/csharp/language-reference/operators/collection-expressions.md @@ -1,7 +1,7 @@ --- title: "Collection expressions (Collection literals)" description: Collection expressions convert to many collection types. You can write literal values, expressions, or other collections to create a new collection. -ms.date: 01/20/2026 +ms.date: 02/04/2026 helpviewer_keywords: - "Collection expressions" --- @@ -68,18 +68,18 @@ When you convert a collection expression to a `Span` or `ReadOnlySpan`, the span ## Collection builder -Collection expressions work with any collection type that's *well-behaved*. A well-behaved collection has the following characteristics: +Collection expressions work with any collection type that is *well-behaved*. A well-behaved collection has the following characteristics: - The value of `Count` or `Length` on a [countable](./member-access-operators.md#index-from-end-operator-) collection produces the same value as the number of elements when enumerated. -- The types in the namespace are presumed to be side-effect free. As such, the compiler can optimize scenarios where such types might be used as intermediary values, but otherwise aren't exposed. -- A call to some applicable `.AddRange(x)` member on a collection results in the same final value as iterating over `x` and adding all of its enumerated values individually to the collection with `.Add`. +- The types in the namespace are side-effect free. The compiler can optimize scenarios where these types might be used as intermediary values, but it doesn't expose them otherwise. +- A call to an applicable `.AddRange(x)` member on a collection results in the same final value as iterating over `x` and adding all of its enumerated values individually to the collection by using `.Add`. All the collection types in the .NET runtime are well-behaved. > [!WARNING] > If a custom collection type isn't well-behaved, the behavior is undefined when you use that collection type with collection expressions. -Your types opt in to collection expression support by writing a `Create()` method and applying the on the collection type to indicate the builder method. For example, consider an application that uses fixed length buffers of 80 characters. That class might look something like the following code: +Your types opt in to collection expression support by writing a `Create()` method and applying the attribute on the collection type to indicate the builder method. For example, consider an application that uses fixed length buffers of 80 characters. That class might look something like the following code: :::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="BufferDeclaration"::: @@ -91,10 +91,61 @@ The `LineBuffer` type implements `IEnumerable`, so the compiler recognizes :::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="BuilderClass"::: -The `Create` method must return a `LineBuffer` object, and it must take a single parameter of the type `ReadOnlySpan`. The type parameter of the `ReadOnlySpan` must match the element type of the collection. A builder method that returns a generic collection has the generic `ReadOnlySpan` as its parameter. The method must be accessible and `static`. +The `Create` method must return a `LineBuffer` object, and it must take a final parameter of the type `ReadOnlySpan`. The type parameter of the `ReadOnlySpan` must match the element type of the collection. A builder method that returns a generic collection has the generic `ReadOnlySpan` as its parameter. The method must be accessible and `static`. + +Starting in C# 15, the `Create` method can have additional parameters before the `ReadOnlySpan` parameter. You can pass values to these parameters by using a `with(...)` element in the collection expression. See [Collection builder arguments](#collection-builder-arguments) for details. Finally, you must add the to the `LineBuffer` class declaration: :::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="BuilderAttribute"::: The first parameter provides the name of the *Builder* class. The second attribute provides the name of the builder method. + +## Collection expression arguments + +Starting in C# 15, you can pass arguments to the underlying collection's constructor or factory method by using a `with(...)` element as the first element in a collection expression. This feature enables you to specify capacity, comparers, or other constructor parameters directly within the collection expression syntax. For more information, see the [collection expression arguments feature specification](~/_csharplang/proposals/collection-expression-arguments.md). + +The `with(...)` element must be the first element in the collection expression. The arguments declared in the `with(...)` element are passed to the appropriate constructor or create method based on the target type. You can use any valid expression for the arguments in the `with` element. + +### Constructor arguments + +When the target type is a class or struct that implements , the arguments in `with(...)` are evaluated and the results are passed to the constructor. The compiler uses overload resolution to select the best matching constructor: + +:::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="WithArgumentsExamples"::: + +In the preceding example: + +- The `List` constructor with a `capacity` parameter is called with `values.Length * 2`. +- The `HashSet` constructor with an parameter is called with `StringComparer.OrdinalIgnoreCase`. +- For interface target types like , the compiler creates a `List` with the specified capacity. + +### Collection builder arguments + +For types with a , the arguments declared in the `with(...)` element are evaluated and the results are passed to the create method *before* the `ReadOnlySpan` parameter. This feature allows create methods to accept configuration parameters: + +:::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="BuilderClassWithComparer"::: + +You can then use the `with(...)` element to pass the comparer: + +:::code language="csharp" source="./snippets/shared/CollectionExpressionExamples.cs" id="WithBuilderArgumentsExample"::: + +The create method is selected using overload resolution based on the arguments provided. The `ReadOnlySpan` containing the collection elements is always the last parameter. + +### Interface target types + +Several interface target types support collection expression arguments. The following table shows the supported interfaces and their applicable constructor signatures: + +| Interface | Supported `with` elements | +|-----------|---------------------| +| , , | `()` (empty only) | +| , | `()`, `(int capacity)` | + +For and , the compiler uses a with the specified constructor. + +### Restrictions + +The `with(...)` element has the following restrictions: + +- It must be the first element in the collection expression. +- Arguments can't have `dynamic` type. +- It's not supported for arrays or span types (`Span`, `ReadOnlySpan`). diff --git a/docs/csharp/language-reference/operators/snippets/shared/CollectionExpressionExamples.cs b/docs/csharp/language-reference/operators/snippets/shared/CollectionExpressionExamples.cs index a96a8be5a77a9..783035c680c86 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/CollectionExpressionExamples.cs +++ b/docs/csharp/language-reference/operators/snippets/shared/CollectionExpressionExamples.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using System.Collections; - // [CollectionBuilder(typeof(LineBufferBuilder), "Create")] // @@ -55,6 +54,35 @@ internal static class LineBufferBuilder } // +// +internal static class MySetBuilder +{ + internal static MySet Create(ReadOnlySpan items) => new MySet(items); + internal static MySet Create(IEqualityComparer comparer, ReadOnlySpan items) => + new MySet(items, comparer); +} +// + +// +[CollectionBuilder(typeof(MySetBuilder), "Create")] +public class MySet : IEnumerable +{ + private readonly HashSet _set; + + public MySet(ReadOnlySpan items, IEqualityComparer? comparer = null) + { + _set = new HashSet(comparer); + foreach (var item in items) + { + _set.Add(item); + } + } + + public IEnumerator GetEnumerator() => _set.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} +// + public class CollectionExpressionExamples { internal static void Examples() @@ -101,6 +129,24 @@ internal static void Examples() "n", "p", "q", "r", "s", "t", "v", "w", "x", "z"]; string[] alphabet = [.. vowels, .. consonants, "y"]; // + + // Test CollectionArgumentsExamples method + Console.WriteLine("\nTesting collection expressions with arguments:"); + var examples = new CollectionExpressionExamples(); + examples.CollectionArgumentsExamples(); + + // Additional test code for collection expressions with arguments + string[] testValues = ["apple", "banana", "cherry"]; + + // Test List with capacity + List fruits = [with(capacity: testValues.Length * 3), .. testValues]; + Console.WriteLine($"List capacity: {fruits.Capacity}, Count: {fruits.Count}"); + Console.WriteLine($"Fruits: {string.Join(", ", fruits)}"); + + // Test HashSet with comparer + HashSet uniqueWords = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "World", "world"]; + Console.WriteLine($"HashSet count (case-insensitive): {uniqueWords.Count}"); + Console.WriteLine($"Unique words: {string.Join(", ", uniqueWords)}"); } @@ -124,5 +170,32 @@ public void Example() } // } + + // + public void CollectionArgumentsExamples() + { + string[] values = ["one", "two", "three"]; + + // Pass capacity argument to List constructor + List names = [with(capacity: values.Length * 2), .. values]; + + // Pass comparer argument to HashSet constructor + HashSet set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"]; + // set contains only one element because all strings are equal with OrdinalIgnoreCase + + // Pass capacity to IList (uses List constructor) + IList numbers = [with(capacity: 100), 1, 2, 3]; + } + // + + // + public void CollectionBuilderArgumentsExample() + { + // Pass comparer to a type with CollectionBuilder attribute + // The comparer argument is passed before the ReadOnlySpan parameter + MySet mySet = [with(StringComparer.OrdinalIgnoreCase), "A", "a", "B"]; + // mySet contains only two elements: "A" and "B" + } + // } diff --git a/docs/csharp/language-reference/operators/snippets/shared/operators.csproj b/docs/csharp/language-reference/operators/snippets/shared/operators.csproj index f4d23c3a13581..5d6cfa5a03119 100644 --- a/docs/csharp/language-reference/operators/snippets/shared/operators.csproj +++ b/docs/csharp/language-reference/operators/snippets/shared/operators.csproj @@ -2,7 +2,8 @@ Exe - net10.0 + net11.0 + preview enable true true diff --git a/docs/csharp/language-reference/operators/with-expression.md b/docs/csharp/language-reference/operators/with-expression.md index 8e71d29fb676d..57dc3f6b62d2d 100644 --- a/docs/csharp/language-reference/operators/with-expression.md +++ b/docs/csharp/language-reference/operators/with-expression.md @@ -2,11 +2,6 @@ title: "The with expression - create new objects that are modified copies of existing objects" description: "Learn about a with expression that performs nondestructive mutation of C# records and structures. The `with` keyword provides the means to modify one or more properties in the new object." ms.date: 01/20/2026 -f1_keywords: - - "with_CSharpKeyword" -helpviewer_keywords: - - "with expression [C#]" - - "with operator [C#]" --- # The `with` expression - Nondestructive mutation creates a new object with modified properties 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 ed0b8890f3cb8..4c66349710ba7 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 @@ -1,31 +1,31 @@ --- -title: "Object and Collection Initializers" +title: "Object and collection initializers" description: Object initializers in C# assign values to accessible fields or properties of an object at creation after invoking a constructor. -ms.date: 10/13/2025 +ms.date: 02/04/2026 helpviewer_keywords: - "object initializers [C#]" - "collection initializers [C#]" --- -# Object and Collection Initializers (C# Programming Guide) +# Object and collection initializers (C# programming guide) -C# lets you instantiate an object or collection and perform member assignments in a single statement. +C# enables you to instantiate an object or collection and perform member assignments in a single statement. ## Object initializers -Object initializers let you 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. The object initializer syntax enables you to specify arguments for a constructor or omit the arguments (and parentheses syntax). The following example shows how to use an object initializer with a named type, `Cat` and how to invoke the parameterless constructor. Note the use of automatically implemented properties in the `Cat` class. For more information, see [Automatically implemented properties](auto-implemented-properties.md). +Object initializers let you assign values to any accessible fields or properties of an object when you create it. You don't need to invoke a constructor and then use assignment statements. The object initializer syntax enables you to specify arguments for a constructor or omit the arguments and parentheses. For guidance on using object initializers consistently, see [Use object initializers (style rule IDE0017)](../../../fundamentals/code-analysis/style-rules/ide0017.md). The following example shows how to use an object initializer with a named type, `Cat`, and how to invoke the parameterless constructor. Note the use of automatically implemented properties in the `Cat` class. For more information, see [Automatically implemented properties](auto-implemented-properties.md). :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="CatDeclaration"::: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ObjectPropertyInitialization"::: -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. +The object initializer syntax allows you to create an instance and assign 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 useful with read-only properties. For more information, see [Object Initializers with class-typed properties](#object-initializers-with-class-typed-properties). +Starting with nested object properties, you can use object initializer syntax without the `new` keyword. This syntax, `Property = { ... }`, enables you to initialize members of existing nested objects, which is useful with read-only properties. For more information, 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"::: -You could initialize the identity matrix with the following code: +You can initialize the identity matrix by using the following code: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="MatrixInitialization"::: @@ -52,33 +52,33 @@ public char this[int i] { set { ... }; } public string this[char c, int i] { set { ... }; } ``` -## Object Initializers with anonymous types +## Object initializers with anonymous types -Although object initializers can be used in any context, they're especially useful in Language-Integrated Query (LINQ) expressions. Query expressions make frequent use of [anonymous types](../../fundamentals/types/anonymous-types.md), which can only be initialized by using an object initializer, as shown in the following declaration. +Although you can use object initializers in any context, they're especially useful in Language-Integrated Query (LINQ) expressions. Query expressions frequently use [anonymous types](../../fundamentals/types/anonymous-types.md), which you can only initialize by using an object initializer, as shown in the following declaration. ```csharp var pet = new { Age = 10, Name = "Fluffy" }; ``` -Anonymous types enable the `select` clause in a LINQ query expression to transform objects of the original sequence into objects whose value and shape can differ from the original. You might want to store only a part of the information from each object in a sequence. In the following example, assume that a product object (`p`) contains many fields and methods, and that you're only interested in creating a sequence of objects that contain the product name and the unit price. +By using anonymous types, the `select` clause in a LINQ query expression can transform objects of the original sequence into objects whose value and shape differ from the original. You might want to store only a part of the information from each object in a sequence. In the following example, assume that a product object (`p`) contains many fields and methods, and that you're only interested in creating a sequence of objects that contain the product name and the unit price. :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="AnonymousUse"::: -When this query is executed, the `productInfos` variable contains a sequence of objects that can be accessed in a `foreach` statement as shown in this example: +When you execute this query, the `productInfos` variable contains a sequence of objects that you can access in a `foreach` statement as shown in this example: ```csharp foreach(var p in productInfos){...} ``` -Each object in the new anonymous type has two public properties that receive the same names as the properties or fields in the original object. You can also rename a field when you're creating an anonymous type; the following example renames the `UnitPrice` field to `Price`. +Each object in the new anonymous type has two public properties that receive the same names as the properties or fields in the original object. You can also rename a field when you create an anonymous type. The following example renames the `UnitPrice` field to `Price`. ```csharp select new {p.ProductName, Price = p.UnitPrice}; ``` -## Object Initializers with the `required` modifier +## Object initializers with the `required` modifier -You use the `required` keyword to force callers to set the value of a property or field using an object initializer. Required properties don't need to be set as constructor parameters. The compiler ensures all callers initialize those values. +Use the `required` keyword to force callers to set the value of a property or field by using an object initializer. You don't need to set required properties as constructor parameters. The compiler ensures all callers initialize those values. ```csharp public class Pet @@ -98,9 +98,9 @@ var pet = new Pet() { Age = 10}; It's a typical practice to guarantee that your object is properly initialized, especially when you have multiple fields or properties to manage and don't want to include them all in the constructor. -## Object Initializers with the `init` accessor +## Object initializers with the `init` accessor -Making sure no one changes the designed object could be limited by using an `init` accessor. It helps to restrict the setting of the property value. +By using an `init` accessor, you can make sure the object doesn't change after initializtion. It helps to restrict the setting of the property value. ```csharp public class Person @@ -124,12 +124,12 @@ pet.FirstName = "Jane"; Required init-only properties support immutable structures while allowing natural syntax for users of the type. -## Object Initializers with class-typed properties +## Object initializers with class-typed properties -When initializing objects with class-typed properties, you can use two different syntaxes: +When you initialize 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() { ... }` +1. **Object initializer with `new` keyword**: `Property = new() { ... }` These syntaxes behave differently. The following example demonstrates both approaches: @@ -137,7 +137,7 @@ These syntaxes behave differently. The following example demonstrates both appro ### 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. +- **Without `new` keyword** (`ClassB = { BI = 100003 }`): This syntax modifies the existing instance of the property that the object constructor created. It calls member initializers on the existing object. - **With `new` keyword** (`ClassB = new() { BI = 100003 }`): This syntax creates a new instance and assigns it to the property, replacing any existing instance. @@ -145,7 +145,7 @@ The initializer without `new` reuses the current instance. In the previous examp ### Object initializers without `new` for read-only properties -The syntax without `new` is useful with read-only properties, where you can't assign a new instance but can still initialize the existing instance's members: +The syntax without `new` is useful with read-only properties. You can't assign a new instance but you can still initialize the existing instance's members: :::code language="csharp" source="./snippets/object-collection-initializers/ObjectInitializerWithoutNew.cs" id="ReadOnlyPropertyExample"::: @@ -153,7 +153,7 @@ This approach allows you to initialize nested objects even when the containing p ## Collection initializers -Collection initializers let you specify one or more element initializers when you initialize a collection type that implements and has `Add` with the appropriate signature as an instance method or an extension method. The element initializers can be a value, an expression, or an object initializer. By using a collection initializer, you don't have to specify multiple calls; the compiler adds the calls automatically. +Collection initializers let you specify one or more element initializers when you initialize a collection type that implements and has an `Add` method with the appropriate signature as an instance method or an extension method. The element initializers can be a value, an expression, or an object initializer. By using a collection initializer, you don't have to specify multiple calls; the compiler adds the calls automatically. For guidance on using collection initializers consistently, see [Use collection initializers (style rule IDE0028)](../../../fundamentals/code-analysis/style-rules/ide0028.md). Collection initializers are also useful in [LINQ queries](../../linq/index.md). The following example shows two simple collection initializers: @@ -174,7 +174,7 @@ You can use a spread element to create one list that copies other list or lists. :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ListInitializerWithSpreadOperator"::: -And include additonal elements along with using a spread element. +And include additional elements along with using a spread element. :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ListInitializerWithSpreadOperatorAndAdditionalElement"::: @@ -182,19 +182,27 @@ You can specify indexed elements if the collection supports read / write indexin :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="DictionaryIndexerInitializer"::: -The preceding sample generates code that calls the to set the values. You could also initialize dictionaries and other associative containers using the following syntax. Notice that instead of indexer syntax, with parentheses and an assignment, it uses an object with multiple values: +The preceding sample generates code that calls the to set the values. You can also initialize dictionaries and other associative containers by using the following syntax. Instead of indexer syntax, with parentheses and an assignment, it uses an object with multiple values: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="DictionaryAddInitializer"::: This initializer example calls to add the three items into the dictionary. These two different ways to initialize associative collections have slightly different behavior because of the method calls the compiler generates. Both variants work with the `Dictionary` class. Other types might only support one or the other based on their public API. -## Object Initializers with collection read-only property initialization +### Collection expression arguments -Some classes might have collection properties where the property is read-only, like the `Cats` property of `CatOwner` in the following case: +Starting in C# 15, use the `with(...)` element as the first element in a [collection expression](../../language-reference/operators/collection-expressions.md) to pass arguments to the collection's constructor. By using this feature, you can specify capacity, comparers, or other constructor parameters directly within the collection expression syntax: + +:::code language="csharp" source="./snippets/object-collection-initializers/CollectionExpressionArguments.cs" id="CollectionExpressionWithArguments"::: + +For more information about collection expression arguments, including supported target types and restrictions, see [Collection expression arguments](../../language-reference/operators/collection-expressions.md#collection-expression-arguments). + +## Object initializers with collection read-only property initialization + +Some classes have collection properties where the property is read-only, like the `Cats` property of `CatOwner` in the following case: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="CatOwnerDeclaration"::: -You can't use collection initializer syntax discussed so far since the property can't be assigned a new list: +You can't use the collection initializer syntax discussed earlier since the property can't be assigned a new list: ```csharp CatOwner owner = new CatOwner @@ -208,11 +216,11 @@ CatOwner owner = new CatOwner }; ``` -However, new entries can be added to `Cats` nonetheless using the initialization syntax by omitting the list creation (`new List`), as shown next: +However, you can add new entries to `Cats` by using the initialization syntax and omitting the list creation (`new List`), as shown in the following example: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ReadOnlyPropertyCollectionInitializer"::: -The set of entries to be added appear surrounded by braces. The preceding code is identical to writing: +The set of entries to add appears surrounded by braces. The preceding code is identical to writing: :::code language="csharp" source="./snippets/object-collection-initializers/BasicObjectInitializers.cs" id="ReadOnlyPropertyCollectionInitializerTranslation"::: @@ -226,13 +234,6 @@ The following example shows an object that implements + public static void CollectionExpressionWithArgumentsExample() + { + string[] values = ["one", "two", "three"]; + + // Use with() to pass capacity to the List constructor + List names = [with(capacity: values.Length * 2), .. values]; + + Console.WriteLine($"Created List with capacity: {names.Capacity}"); + Console.WriteLine($"List contains {names.Count} elements: [{string.Join(", ", names)}]"); + + // Use with() to pass a comparer to the HashSet constructor + HashSet caseInsensitiveSet = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO"]; + // caseInsensitiveSet contains only one element because "Hello" and "HELLO" are equal + + Console.WriteLine($"HashSet with case-insensitive comparer contains {caseInsensitiveSet.Count} elements: [{string.Join(", ", caseInsensitiveSet)}]"); + Console.WriteLine("Note: 'Hello' and 'HELLO' are treated as the same element due to OrdinalIgnoreCase comparer"); + } + // +} diff --git a/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/object-collection-initializers.csproj b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/object-collection-initializers.csproj index f57e231d581e5..3dd077b3a0f9e 100644 --- a/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/object-collection-initializers.csproj +++ b/docs/csharp/programming-guide/classes-and-structs/snippets/object-collection-initializers/object-collection-initializers.csproj @@ -2,7 +2,8 @@ Exe - net8.0 + net11.0 + preview enable enable object_collection_initializers diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index bac4c4c1b2468..2286c56360af5 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -147,6 +147,8 @@ items: href: ../../../_csharplang/proposals/csharp-12.0/collection-expressions.md - name: Better conversion from collection expression href: ../../../_csharplang/proposals/csharp-13.0/collection-expressions-better-conversion.md + - name: Collection expression arguments + href: ../../../_csharplang/proposals/collection-expression-arguments.md - name: Lambda discard parameters href: ../../../_csharplang/proposals/csharp-9.0/lambda-discard-parameters.md - name: Static anonymous functions diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index 5324dd7a241eb..feb328293414b 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -142,6 +142,10 @@ items: href: fundamentals/exceptions/how-to-execute-cleanup-code-using-finally.md - name: What's new in C# items: + - name: C# 15 + items: + - name: What's new in C# 15 + href: whats-new/csharp-15.md - name: C# 14 items: - name: What's new in C# 14 @@ -156,6 +160,8 @@ items: href: whats-new/csharp-12.md - name: Breaking changes items: + - name: After C# 14 + href: ../../_roslyn/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%2011.md - name: After C# 13 href: ../../_roslyn/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%2010.md - name: After C# 12 diff --git a/docs/csharp/whats-new/csharp-15.md b/docs/csharp/whats-new/csharp-15.md new file mode 100644 index 0000000000000..3b0db85177798 --- /dev/null +++ b/docs/csharp/whats-new/csharp-15.md @@ -0,0 +1,48 @@ +--- +title: What's new in C# 15 +description: Get an overview of the new features in C# 15. C# 15 ships with .NET 11. +ms.date: 02/04/2026 +ms.topic: whats-new +ms.update-cycle: 365-days +ai-usage: ai-assisted +--- +# What's new in C# 15 + +C# 15 includes the following new features. You can try these features using the latest [Visual Studio 2026](https://visualstudio.microsoft.com/) version or the [.NET 11 preview SDK](https://dotnet.microsoft.com/download/dotnet): + +- [Collection expression arguments](#collection-expression-arguments) + +C# 15 is the latest C# release. C# 15 is supported on **.NET 11**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). + +You can download the latest .NET 11 preview SDK from the [.NET downloads page](https://dotnet.microsoft.com/download). You can also download [Visual Studio 2026 insiders](https://visualstudio.microsoft.com/vs/), which includes the .NET 11 preview SDK. + +New features are added to the "What's new in C#" page when they're available in public preview releases. The [working set](https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md#working-set) section of the [roslyn feature status page](https://github.com/dotnet/roslyn/blob/main/docs/Language%20Feature%20Status.md) tracks when upcoming features are merged into the main branch. + +You can find any breaking changes introduced in C# 15 in our article on [breaking changes](~/_roslyn/docs/compilers/CSharp/Compiler%20Breaking%20Changes%20-%20DotNet%2011.md). + +[!INCLUDE [released-version-feedback](./includes/released-feedback.md)] + +## Collection expression arguments + +You can pass arguments to the underlying collection's constructor or factory method by using a `with(...)` element as the first element in a collection expression. This feature enables you to specify capacity, comparers, or other constructor parameters directly within the collection expression syntax. + +The following example shows how to pass a capacity argument to a `List` constructor and a comparer to a `HashSet`: + +```csharp +string[] values = ["one", "two", "three"]; + +// Pass capacity argument to List constructor +List names = [with(capacity: values.Length * 2), .. values]; + +// Pass comparer argument to HashSet constructor +HashSet set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"]; +// set contains only one element because all strings are equal with OrdinalIgnoreCase +``` + +You can learn more about collection expression arguments in the [language reference article on collection expressions](../language-reference/operators/collection-expressions.md#collection-expression-arguments) or the [feature specification](~/_csharplang/proposals/collection-expression-arguments.md). For information on using collection expression arguments in collection initializers, see [Object and Collection Initializers](../programming-guide/classes-and-structs/object-and-collection-initializers.md#collection-expression-arguments). + +