|
| 1 | +--- |
| 2 | +title: Explore extension members in C# 14 to enhance existing types |
| 3 | +description: "C# 14 provides new syntax for extensions that support properties and operators, and enables extensions on a type as well as an instance. Learn to use them, and how to migrate existing extension methods to extension members" |
| 4 | +author: billwagner |
| 5 | +ms.author: wiwagn |
| 6 | +ms.service: dotnet-csharp |
| 7 | +ms.topic: tutorial |
| 8 | +ms.date: 10/06/2025 |
| 9 | +ai-usage: ai-assisted |
| 10 | +#customer intent: As a C# developer, I reduce repeated code by introducing extension members for common tasks |
| 11 | +--- |
| 12 | +# Tutorial: Explore extension members in C# 14 |
| 13 | + |
| 14 | +C# 14 introduces extension members, an enhancement to the existing extension methods. Extension members enable you to add properties and operators. You can also extend types in addition to instances of types. This capability allows you to create more natural and expressive APIs when extending types you don't control. |
| 15 | + |
| 16 | +In this tutorial, you explore extension members by enhancing the `System.Drawing.Point` type with mathematical operations, coordinate transformations, and utility properties. You learn how to migrate existing extension methods to the new extension member syntax and understand when to use each approach. |
| 17 | + |
| 18 | +In this tutorial, you: |
| 19 | + |
| 20 | +> [!div class="checklist"] |
| 21 | +> |
| 22 | +> * Create extension members with static properties and operators. |
| 23 | +> * Implement coordinate transformations using extension members. |
| 24 | +> * Migrate traditional extension methods to extension member syntax. |
| 25 | +> * Compare extension members with traditional extension methods. |
| 26 | +
|
| 27 | +## Prerequisites |
| 28 | + |
| 29 | +- The .NET 10 preview SDK. Download it from the [.NET download site](https://dotnet.microsoft.com/download/dotnet/10.0). |
| 30 | +- Visual Studio 2026 (preview). Download it from the [Visual Studio insiders page](https://visualstudio.microsoft.com/insiders/). |
| 31 | + |
| 32 | +## Create the sample application |
| 33 | + |
| 34 | +Start by creating a console application that demonstrates both traditional extension methods and the new extension members syntax. |
| 35 | + |
| 36 | +1. Create a new console application: |
| 37 | + |
| 38 | + ```dotnetcli |
| 39 | + dotnet new console -n PointExtensions |
| 40 | + cd PointExtensions |
| 41 | + ``` |
| 42 | + |
| 43 | +1. Copy the following code into a new file named `ExtensionMethods.cs`: |
| 44 | + |
| 45 | + :::code language="csharp" source="snippets/PointExtensions/ExtensionMethods.cs"::: |
| 46 | + |
| 47 | +1. Copy the following code that demonstrates these extension methods and other uses of the <xref:System.Drawing.Point?displayProperty=nameWithType>: |
| 48 | + |
| 49 | + :::code language="csharp" source="snippets/PointExtensions/ExtensionMethodsDemonstrations.cs"::: |
| 50 | + |
| 51 | +1. Replace the content of `Program.cs` with the demonstration code: |
| 52 | + |
| 53 | + :::code language="csharp" source="snippets/PointExtensions/Program.cs" id="TraditionalExtensionMethods"::: |
| 54 | + |
| 55 | +1. Run the sample application and examine the output. |
| 56 | + |
| 57 | +Traditional extension methods can only add instance methods to existing types. Extension members enable you to add static properties, which provides a more natural way to extend types with constants or computed values. |
| 58 | + |
| 59 | +## Add static properties with extension members |
| 60 | + |
| 61 | +First, examine this code in the sample: |
| 62 | + |
| 63 | +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="StaticPropertySubstitute"::: |
| 64 | + |
| 65 | +Many apps that use 2D geometry use the concept of an `Origin`, which is the same value as <xref:System.Drawing.Point.Empty?displayProperty=nameWithType>. This code uses that fact, but some developers might create a `new Point(0,0)`, which incurs some extra work. In a given domain, you want to express these common values through static properties. |
| 66 | + |
| 67 | +Create `NewExtensionsMembers.cs` to create extension members that solve this problem: |
| 68 | + |
| 69 | +```csharp |
| 70 | +using System.Drawing; |
| 71 | +using System.Numerics; |
| 72 | + |
| 73 | +namespace ExtensionMembers; |
| 74 | + |
| 75 | +public static class PointExtensions |
| 76 | +{ |
| 77 | + extension (Point) |
| 78 | + { |
| 79 | + public static Point Origin => Point.Empty; |
| 80 | + } |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +The preceding code adds a static *extension property* to the `Point` struct. The `extension` keyword introduces an *extension block*. This extension block extends the `Point` struct. |
| 85 | + |
| 86 | +You can use this static property as though it were a member of the `Point` struct. |
| 87 | + |
| 88 | +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="StaticProperty"::: |
| 89 | + |
| 90 | +The `Point.Origin` property now appears as if it were part of the original `Point` type, providing a more intuitive API. |
| 91 | + |
| 92 | +## Implement arithmetic operators |
| 93 | + |
| 94 | +Next, examine the following code that performs arithmetic with points: |
| 95 | + |
| 96 | +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="PointArithmetic"::: |
| 97 | + |
| 98 | +Traditional extension methods can't add operators to existing types. You must implement arithmetic operations manually, making the code verbose and harder to read. The algorithm gets duplicated whenever the operation is needed, which creates more opportunities for small mistakes to enter the code base. It's better to place that code in one location. Add the following operators to your extension block in `NewExtensionsMembers.cs`: |
| 99 | + |
| 100 | +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="ArithmeticOperators"::: |
| 101 | + |
| 102 | +Extension members enable you to add operators directly to existing types. Now you can perform arithmetic operations using natural syntax: |
| 103 | + |
| 104 | +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="PointArithmeticWithOperators"::: |
| 105 | + |
| 106 | +The extension operators make point arithmetic as natural as working with built-in numeric types. |
| 107 | + |
| 108 | +## Add more operators |
| 109 | + |
| 110 | +You can also add extension operators for the discrete operations shown in the following code example: |
| 111 | + |
| 112 | +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="DiscreteXYOperators"::: |
| 113 | + |
| 114 | +The `+` and `-` operators are *binary operators* and require two operands, not three. Instead of two discrete integers, use a *tuple* to specify both the `X` and `Y` deltas: |
| 115 | + |
| 116 | +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="TupleBasedXYOperators"::: |
| 117 | + |
| 118 | +The preceding operator enables elegant tuple-based operations: |
| 119 | + |
| 120 | +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="DiscreteXYOperators"::: |
| 121 | + |
| 122 | +Your extensions can include multiple overloaded operators, as long as the operands are distinct. |
| 123 | + |
| 124 | +## Migrate instance methods to extension members |
| 125 | + |
| 126 | +Extension members also support instance methods. You don't have to change existing extension methods. The old and new forms are binary and source compatible. If you want to keep all your extensions in one container, you can. Migrating traditional extension methods to the new syntax maintains the same functionality. |
| 127 | + |
| 128 | +### Traditional extension methods |
| 129 | + |
| 130 | +The traditional approach uses the `this` parameter syntax: |
| 131 | + |
| 132 | +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="ExtensionMethods"::: |
| 133 | + |
| 134 | +Extension members use a different syntax but provide the same functionality. Add the following code to your new extension members class: |
| 135 | + |
| 136 | +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="TransformationMethods"::: |
| 137 | + |
| 138 | +The preceding code doesn't compile yet. It's the first extension you wrote that extends an *instance* of the `Point` class, instead of the type itself. To support instance extensions, your extension block needs to name the receiver parameter. Edit the following line: |
| 139 | + |
| 140 | +```csharp |
| 141 | + extension (Point) |
| 142 | +``` |
| 143 | + |
| 144 | +So that it gives a name to the `Point` instance: |
| 145 | + |
| 146 | +```csharp |
| 147 | + extension (Point point) |
| 148 | +``` |
| 149 | + |
| 150 | +Now, the code compiles. You can call these new instance methods exactly as you accessed traditional extension methods: |
| 151 | + |
| 152 | +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="InstanceMethods"::: |
| 153 | + |
| 154 | +The key difference is syntax: extension members use `extension (Type variableName)` instead of `this Type variableName`. |
| 155 | + |
| 156 | +## Completed sample |
| 157 | + |
| 158 | +The final example shows the advantages when you combine static properties, operators, and instance methods to create comprehensive type extensions. |
| 159 | + |
| 160 | +Compare the extension member version: |
| 161 | + |
| 162 | +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="FinalScenarios"::: |
| 163 | + |
| 164 | +With the previous version: |
| 165 | + |
| 166 | +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="FinalScenarios"::: |
| 167 | + |
| 168 | +This example demonstrates how extension members create a cohesive API that feels like part of the original type. You can: |
| 169 | + |
| 170 | +- Use `Point.Origin` for a meaningful starting point |
| 171 | +- Apply mathematical operators naturally (`point + offset`, `point * scale`) |
| 172 | +- Chain transformations using both operators and methods |
| 173 | +- Convert between related types (`ToVector()`) |
| 174 | + |
| 175 | +### Migration benefits |
| 176 | + |
| 177 | +When migrating from traditional extension methods to extension members, you gain: |
| 178 | + |
| 179 | +1. **Static properties**: Add constants and computed values to types. |
| 180 | +1. **Operators**: Enable natural mathematical and logical operations. |
| 181 | +1. **Unified syntax**: All extension logic uses the same `extension` declaration. |
| 182 | +1. **Type-level extensions**: Extend the type itself, not just instances. |
| 183 | + |
| 184 | +Run the complete application to see both approaches side by side and observe how extension members provide a more integrated development experience. |
| 185 | + |
| 186 | +## Related content |
| 187 | + |
| 188 | +- [Extension methods (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) |
| 189 | +- [What's new in C# 14](/dotnet/csharp/whats-new/csharp-14) |
| 190 | +- [Operator overloading (C# reference)](/dotnet/csharp/language-reference/operators/operator-overloading) |
0 commit comments