From 499ba593d3402d0280567e368a1f8a5b63c21fc6 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 16 Sep 2025 14:34:22 -0400 Subject: [PATCH 1/2] Add Extension operators Fixes #47268 Fixes #47280 Extension indexers didn't make C# 14 (although properties did). Remove those examples. Add examples and discussion for extension operators, which are available in RC1. --- docs/csharp/language-reference/keywords/extension.md | 10 ++++++---- .../language-reference/keywords/snippets/Extensions.cs | 5 ++++- .../classes-and-structs/extension-methods.md | 6 ++++-- docs/csharp/whats-new/csharp-14.md | 9 +++++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/docs/csharp/language-reference/keywords/extension.md b/docs/csharp/language-reference/keywords/extension.md index eba46e1d17b85..5f0627cb54559 100644 --- a/docs/csharp/language-reference/keywords/extension.md +++ b/docs/csharp/language-reference/keywords/extension.md @@ -1,16 +1,16 @@ --- title: "Extension member declarations" description: "Learn the syntax to declare extension members in C#. Extension members enable you to add functionality to types and interfaces in those instances where you don't have the source for the original type. Extensions are often paired with generic interfaces to implement a common set of functionality across all types that implement that interface." -ms.date: 04/17/2025 +ms.date: 09/17/2025 f1_keywords: - "extension_CSharpKeyword" - "extension" --- # Extension declaration (C# Reference) -Beginning with C# 14, top level, nongeneric `static class` declarations can use `extension` containers to declare *extension members*. Extension members are methods or properties and can appear to be instance or static members. Earlier versions of C# enable *extension methods* by adding `this` as a modifier to the first parameter of a static method declared in a top-level, nongeneric static class. +Beginning with C# 14, top level, nongeneric `static class` declarations can use `extension` blocks to declare *extension members*. Extension members are methods or properties and can appear to be instance or static members. Earlier versions of C# enable *extension methods* by adding `this` as a modifier to the first parameter of a static method declared in a top-level, nongeneric static class. -The `extension` block specifies the type and receiver for extension members. You can declare methods and properties inside the `extension` declaration. The following example declares a single extension block that defines an instance extension method and an instance property. +The `extension` block specifies the type and receiver for extension members. You can declare methods, properties or operators inside the `extension` declaration. The following example declares a single extension block that defines an instance extension method, an instance property, and a static operator method. :::code language="csharp" source="./snippets/extensions.cs" id="ExtensionMembers"::: @@ -20,7 +20,7 @@ Any of the extension members can be accessed as though they were members of the :::code language="csharp" source="./snippets/extensions.cs" id="UseExtensionMethod"::: -You can declare any number of members in a single container, as long as they share the same receiver. You can declare as many extension blocks in a single class as well. Different extensions don't need to declare the same type or name of receiver. The extension parameter doesn't need to include the parameter name if the only members are static: +You can declare any number of members in a single block, as long as they share the same receiver. You can declare as many extension blocks in a single class as well. Different extensions don't need to declare the same type or name of receiver. The extension parameter doesn't need to include the parameter name if the only members are static: :::code language="csharp" source="./snippets/extensions.cs" id="StaticExtensions"::: @@ -28,6 +28,8 @@ Static extensions can be called as though they're static members of the receiver :::code language="csharp" source="./snippets/extensions.cs" id="UseStaticExtensions"::: +Operators are called as though they are user defined operators on the type. + > [!IMPORTANT] > An extension doesn't introduce a *scope* for member declarations. All members declared in a single class, even if in multiple extensions, must have unique signatures. The generated signature includes the receiver type in its name for static members and the receiver parameter for extension instance members. diff --git a/docs/csharp/language-reference/keywords/snippets/Extensions.cs b/docs/csharp/language-reference/keywords/snippets/Extensions.cs index 90dd06c611058..626c7556e963b 100644 --- a/docs/csharp/language-reference/keywords/snippets/Extensions.cs +++ b/docs/csharp/language-reference/keywords/snippets/Extensions.cs @@ -35,7 +35,8 @@ public int Median } } - public int this[int index] => sequence.Skip(index).First(); + public static IEnumerable operator +(IEnumerable left, IEnumerable right) + => left.Concat(right); } } // @@ -102,6 +103,8 @@ public static void BasicExample() numbers = numbers.AddValue(10); var median = numbers.Median; + + var combined = numbers + Enumerable.Range(100, 10); // // diff --git a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md index c568b72a3d92e..299c01d042f44 100644 --- a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md +++ b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md @@ -1,7 +1,7 @@ --- title: "Extension members" description: Extension members in C# enable you to add methods, properties, or operators to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. -ms.date: 04/15/2025 +ms.date: 09/17/2025 helpviewer_keywords: - "methods [C#], adding to existing types" - "extension methods [C#]" @@ -12,7 +12,9 @@ helpviewer_keywords: Extension members enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. -Beginning with C# 14, there are two syntaxes you use to define extension methods. C# 14 adds [`extension`](../../language-reference/keywords/extension.md) containers, where you define multiple extension members for a type or an instance of a type. Before C# 14, you add the [`this`](../../language-reference/keywords/this.md) modifier to the first parameter of a static method to indicate that the method appears as a member of an instance of the parameter type. +Beginning with C# 14, there are two syntaxes you use to define extension methods. C# 14 adds [`extension`](../../language-reference/keywords/extension.md) blocks, where you define multiple extension members for a type or an instance of a type. Before C# 14, you add the [`this`](../../language-reference/keywords/this.md) modifier to the first parameter of a static method to indicate that the method appears as a member of an instance of the parameter type. + +Extension blocks support multiple member types: methods, properties, and operators. With extension blocks, you can define both instance extensions and static extensions. Instance extensions extend an instance of the type; static extensions extend the type itself. The form of extension methods declared with the `this` modifier supports instance extension methods. Extension methods are static methods, but they're called as if they were instance methods on the extended type. For client code written in C#, F# and Visual Basic, there's no apparent difference between calling an extension method and the methods defined in a type. Both forms of extension methods are compiled to the same IL (Intermediate Language). Consumers of extension members don't need to know which syntax was used to define extension methods. diff --git a/docs/csharp/whats-new/csharp-14.md b/docs/csharp/whats-new/csharp-14.md index 09e4ab942ceb9..d63573bc62c3a 100644 --- a/docs/csharp/whats-new/csharp-14.md +++ b/docs/csharp/whats-new/csharp-14.md @@ -1,7 +1,7 @@ --- title: What's new in C# 14 description: Get an overview of the new features in C# 14. C# 14 ships with .NET 10. -ms.date: 04/17/2025 +ms.date: 09/17/2025 ms.topic: whats-new ms.update-cycle: 180-days --- @@ -30,7 +30,7 @@ You can find any breaking changes introduced in C# 14 in our article on [breakin ## Extension members -C# 14 adds new syntax to define *extension members*. The new syntax enables you to declare *extension properties* in addition to extension methods. You can also declare extension members that extend the type, rather than an instance of the type. In other words, these new extension members can appear as static members of the type you extend. The following code example shows an example of the different kinds of extension members you can declare: +C# 14 adds new syntax to define *extension members*. The new syntax enables you to declare *extension properties* in addition to extension methods. You can also declare extension members that extend the type, rather than an instance of the type. In other words, these new extension members can appear as static members of the type you extend. These extensions can include user defined operators implemented as static extension methods. The following code example shows an example of the different kinds of extension members you can declare: ```csharp public static class Enumerable @@ -40,8 +40,6 @@ public static class Enumerable { // Extension property: public bool IsEmpty => !source.Any(); - // Extension indexer: - public TSource this[int index] => source.Skip(index).First(); // Extension method: public IEnumerable Where(Func predicate) { ... } @@ -55,6 +53,9 @@ public static class Enumerable // static extension property: public static IEnumerable Identity => Enumerable.Empty(); + + // static user defined operator: + public static IEnumerable operator + (IEnumerable left, IEnumerable right) => left.Concat(right); } } ``` From 331b5b2a39b9e1bf6eba3d560a393ecfeda17f0c Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 16 Sep 2025 14:39:27 -0400 Subject: [PATCH 2/2] proofread --- docs/csharp/language-reference/keywords/extension.md | 4 ++-- .../classes-and-structs/extension-methods.md | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/csharp/language-reference/keywords/extension.md b/docs/csharp/language-reference/keywords/extension.md index 5f0627cb54559..24fbed96d8dd5 100644 --- a/docs/csharp/language-reference/keywords/extension.md +++ b/docs/csharp/language-reference/keywords/extension.md @@ -10,7 +10,7 @@ f1_keywords: Beginning with C# 14, top level, nongeneric `static class` declarations can use `extension` blocks to declare *extension members*. Extension members are methods or properties and can appear to be instance or static members. Earlier versions of C# enable *extension methods* by adding `this` as a modifier to the first parameter of a static method declared in a top-level, nongeneric static class. -The `extension` block specifies the type and receiver for extension members. You can declare methods, properties or operators inside the `extension` declaration. The following example declares a single extension block that defines an instance extension method, an instance property, and a static operator method. +The `extension` block specifies the type and receiver for extension members. You can declare methods, properties, or operators inside the `extension` declaration. The following example declares a single extension block that defines an instance extension method, an instance property, and a static operator method. :::code language="csharp" source="./snippets/extensions.cs" id="ExtensionMembers"::: @@ -28,7 +28,7 @@ Static extensions can be called as though they're static members of the receiver :::code language="csharp" source="./snippets/extensions.cs" id="UseStaticExtensions"::: -Operators are called as though they are user defined operators on the type. +Operators are called as though they're user defined operators on the type. > [!IMPORTANT] > An extension doesn't introduce a *scope* for member declarations. All members declared in a single class, even if in multiple extensions, must have unique signatures. The generated signature includes the receiver type in its name for static members and the receiver parameter for extension instance members. diff --git a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md index 299c01d042f44..f5588bc085b3b 100644 --- a/docs/csharp/programming-guide/classes-and-structs/extension-methods.md +++ b/docs/csharp/programming-guide/classes-and-structs/extension-methods.md @@ -89,7 +89,7 @@ In the past, it was common to create "Collection Classes" that implemented the < ### Layer-Specific Functionality -When using an Onion Architecture or other layered application design, it's common to have a set of Domain Entities or Data Transfer Objects that can be used to communicate across application boundaries. These objects generally contain no functionality, or only minimal functionality that applies to all layers of the application. Extension methods can be used to add functionality that is specific to each application layer. +When using an Onion Architecture or other layered application design, it's common to have a set of Domain Entities or Data Transfer Objects that can be used to communicate across application boundaries. These objects generally contain no functionality, or only minimal functionality that applies to all layers of the application. Extension methods can be used to add functionality that's specific to each application layer. :::code language="csharp" source="./snippets/ExtensionMembers/CustomExtensionMethods.cs" id="DomainEntity"::: @@ -101,7 +101,7 @@ You can declare an equivalent `FullName` property in C# 14 and later using the n Rather than creating new objects when reusable functionality needs to be created, you can often extend an existing type, such as a .NET or CLR type. As an example, if you don't use extension methods, you might create an `Engine` or `Query` class to do the work of executing a query on a SQL Server that might be called from multiple places in our code. However you can instead extend the class using extension methods to perform that query from anywhere you have a connection to a SQL Server. Other examples might be to add common functionality to the class, extend the data processing capabilities of the object, and objects for specific error handling functionality. These types of use-cases are limited only by your imagination and good sense. -Extending predefined types can be difficult with `struct` types because they're passed by value to methods. That means any changes to the struct are made to a copy of the struct. Those changes aren't visible once the extension method exits. You can add the `ref` modifier to the first argument making it a `ref` extension method. The `ref` keyword can appear before or after the `this` keyword without any semantic differences. Adding the `ref` modifier indicates that the first argument is passed by reference. This technique enables you to write extension methods that change the state of the struct being extended (note that private members aren't accessible). Only value types or generic types constrained to struct (For more information about these rules, see [`struct` constraint](../../language-reference/builtin-types/struct.md#struct-constraint) for more information) are allowed as the first parameter of a `ref` extension method or as the receiver of an extension block. The following example shows how to use a `ref` extension method to directly modify a built-in type without the need to reassign the result or pass it through a function with the `ref` keyword: +Extending predefined types can be difficult with `struct` types because they're passed by value to methods. That means any changes to the struct are made to a copy of the struct. Those changes aren't visible once the extension method exits. You can add the `ref` modifier to the first argument making it a `ref` extension method. The `ref` keyword can appear before or after the `this` keyword without any semantic differences. Adding the `ref` modifier indicates that the first argument is passed by reference. This technique enables you to write extension methods that change the state of the struct being extended (note that private members aren't accessible). Only value types or generic types constrained to struct (For more information about these rules, see the article on the [`struct` constraint](../../language-reference/builtin-types/struct.md#struct-constraint)) are allowed as the first parameter of a `ref` extension method or as the receiver of an extension block. The following example shows how to use a `ref` extension method to directly modify a built-in type without the need to reassign the result or pass it through a function with the `ref` keyword: :::code language="csharp" source="./snippets/ExtensionMembers/CustomExtensionMethods.cs" id="RefExtensions"::: @@ -145,7 +145,3 @@ For a class library that you implemented, you shouldn't use extension methods to - [Parallel Programming Samples (many examples demonstrate extension methods)](/samples/browse/?products=dotnet&term=parallel) - [Lambda Expressions](../../language-reference/operators/lambda-expressions.md) - [Standard Query Operators Overview](../../linq/standard-query-operators/index.md) -- [Conversion rules for Instance parameters and their impact](/archive/blogs/sreekarc/conversion-rules-for-instance-parameters-and-their-impact) -- [Extension methods Interoperability between languages](/archive/blogs/sreekarc/extension-methods-interoperability-between-languages) -- [Extension methods and Curried Delegates](/archive/blogs/sreekarc/extension-methods-and-curried-delegates) -- [Extension method Binding and Error reporting](/archive/blogs/sreekarc/extension-method-binding-and-error-reporting)