Skip to content

Commit 96ca689

Browse files
authored
Add Extension operators (#48550)
* 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. * proofread
1 parent beab5b1 commit 96ca689

File tree

4 files changed

+21
-17
lines changed

4 files changed

+21
-17
lines changed

docs/csharp/language-reference/keywords/extension.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
---
22
title: "Extension member declarations"
33
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."
4-
ms.date: 04/17/2025
4+
ms.date: 09/17/2025
55
f1_keywords:
66
- "extension_CSharpKeyword"
77
- "extension"
88
---
99
# Extension declaration (C# Reference)
1010

11-
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.
11+
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.
1212

13-
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.
13+
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.
1414

1515
:::code language="csharp" source="./snippets/extensions.cs" id="ExtensionMembers":::
1616

@@ -20,14 +20,16 @@ Any of the extension members can be accessed as though they were members of the
2020

2121
:::code language="csharp" source="./snippets/extensions.cs" id="UseExtensionMethod":::
2222

23-
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:
23+
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:
2424

2525
:::code language="csharp" source="./snippets/extensions.cs" id="StaticExtensions":::
2626

2727
Static extensions can be called as though they're static members of the receiver type:
2828

2929
:::code language="csharp" source="./snippets/extensions.cs" id="UseStaticExtensions":::
3030

31+
Operators are called as though they're user defined operators on the type.
32+
3133
> [!IMPORTANT]
3234
> 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.
3335

docs/csharp/language-reference/keywords/snippets/Extensions.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ public int Median
3535
}
3636
}
3737

38-
public int this[int index] => sequence.Skip(index).First();
38+
public static IEnumerable<int> operator +(IEnumerable<int> left, IEnumerable<int> right)
39+
=> left.Concat(right);
3940
}
4041
}
4142
// </ExtensionMembers>
@@ -102,6 +103,8 @@ public static void BasicExample()
102103
numbers = numbers.AddValue(10);
103104

104105
var median = numbers.Median;
106+
107+
var combined = numbers + Enumerable.Range(100, 10);
105108
// </UseExtensionMethod>
106109

107110
// <UseStaticExtensions>

docs/csharp/programming-guide/classes-and-structs/extension-methods.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Extension members"
33
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.
4-
ms.date: 04/15/2025
4+
ms.date: 09/17/2025
55
helpviewer_keywords:
66
- "methods [C#], adding to existing types"
77
- "extension methods [C#]"
@@ -12,7 +12,9 @@ helpviewer_keywords:
1212

1313
Extension members enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.
1414

15-
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.
15+
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.
16+
17+
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.
1618

1719
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.
1820

@@ -87,7 +89,7 @@ In the past, it was common to create "Collection Classes" that implemented the <
8789

8890
### Layer-Specific Functionality
8991

90-
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.
92+
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.
9193

9294
:::code language="csharp" source="./snippets/ExtensionMembers/CustomExtensionMethods.cs" id="DomainEntity":::
9395

@@ -99,7 +101,7 @@ You can declare an equivalent `FullName` property in C# 14 and later using the n
99101

100102
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 <xref:System.Data.SqlClient.SqlConnection?displayProperty=nameWithType> 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 <xref:System.String?displayProperty=nameWithType> class, extend the data processing capabilities of the <xref:System.IO.Stream?displayProperty=nameWithType> object, and <xref:System.Exception?displayProperty=nameWithType> objects for specific error handling functionality. These types of use-cases are limited only by your imagination and good sense.
101103

102-
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:
104+
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:
103105

104106
:::code language="csharp" source="./snippets/ExtensionMembers/CustomExtensionMethods.cs" id="RefExtensions":::
105107

@@ -143,7 +145,3 @@ For a class library that you implemented, you shouldn't use extension methods to
143145
- [Parallel Programming Samples (many examples demonstrate extension methods)](/samples/browse/?products=dotnet&term=parallel)
144146
- [Lambda Expressions](../../language-reference/operators/lambda-expressions.md)
145147
- [Standard Query Operators Overview](../../linq/standard-query-operators/index.md)
146-
- [Conversion rules for Instance parameters and their impact](/archive/blogs/sreekarc/conversion-rules-for-instance-parameters-and-their-impact)
147-
- [Extension methods Interoperability between languages](/archive/blogs/sreekarc/extension-methods-interoperability-between-languages)
148-
- [Extension methods and Curried Delegates](/archive/blogs/sreekarc/extension-methods-and-curried-delegates)
149-
- [Extension method Binding and Error reporting](/archive/blogs/sreekarc/extension-method-binding-and-error-reporting)

docs/csharp/whats-new/csharp-14.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: What's new in C# 14
33
description: Get an overview of the new features in C# 14. C# 14 ships with .NET 10.
4-
ms.date: 04/17/2025
4+
ms.date: 09/17/2025
55
ms.topic: whats-new
66
ms.update-cycle: 180-days
77
---
@@ -30,7 +30,7 @@ You can find any breaking changes introduced in C# 14 in our article on [breakin
3030

3131
## Extension members
3232

33-
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:
33+
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:
3434

3535
```csharp
3636
public static class Enumerable
@@ -40,8 +40,6 @@ public static class Enumerable
4040
{
4141
// Extension property:
4242
public bool IsEmpty => !source.Any();
43-
// Extension indexer:
44-
public TSource this[int index] => source.Skip(index).First();
4543

4644
// Extension method:
4745
public IEnumerable<TSource> Where(Func<TSource, bool> predicate) { ... }
@@ -55,6 +53,9 @@ public static class Enumerable
5553

5654
// static extension property:
5755
public static IEnumerable<TSource> Identity => Enumerable.Empty<TSource>();
56+
57+
// static user defined operator:
58+
public static IEnumerable<TSource> operator + (IEnumerable<TSource> left, IEnumerable<TSource> right) => left.Concat(right);
5859
}
5960
}
6061
```

0 commit comments

Comments
 (0)