diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index 4192b1deb3902..be2c11619067a 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -174,6 +174,8 @@ items: href: whats-new/version-update-considerations.md - name: Tutorials items: + - name: Explore extension members + href: whats-new/tutorials/extension-members.md - name: Explore compound assignment href: whats-new/tutorials/compound-assignment-operators.md - name: Explore primary constructors diff --git a/docs/csharp/whats-new/tutorials/extension-members.md b/docs/csharp/whats-new/tutorials/extension-members.md new file mode 100644 index 0000000000000..f7754ddff9a20 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/extension-members.md @@ -0,0 +1,190 @@ +--- +title: Explore extension members in C# 14 to enhance existing types +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" +author: billwagner +ms.author: wiwagn +ms.service: dotnet-csharp +ms.topic: tutorial +ms.date: 10/06/2025 +ai-usage: ai-assisted +#customer intent: As a C# developer, I reduce repeated code by introducing extension members for common tasks +--- +# Tutorial: Explore extension members in C# 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. + +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. + +In this tutorial, you: + +> [!div class="checklist"] +> +> * Create extension members with static properties and operators. +> * Implement coordinate transformations using extension members. +> * Migrate traditional extension methods to extension member syntax. +> * Compare extension members with traditional extension methods. + +## Prerequisites + +- The .NET 10 preview SDK. Download it from the [.NET download site](https://dotnet.microsoft.com/download/dotnet/10.0). +- Visual Studio 2026 (preview). Download it from the [Visual Studio insiders page](https://visualstudio.microsoft.com/insiders/). + +## Create the sample application + +Start by creating a console application that demonstrates both traditional extension methods and the new extension members syntax. + +1. Create a new console application: + + ```dotnetcli + dotnet new console -n PointExtensions + cd PointExtensions + ``` + +1. Copy the following code into a new file named `ExtensionMethods.cs`: + + :::code language="csharp" source="snippets/PointExtensions/ExtensionMethods.cs"::: + +1. Copy the following code that demonstrates these extension methods and other uses of the : + + :::code language="csharp" source="snippets/PointExtensions/ExtensionMethodsDemonstrations.cs"::: + +1. Replace the content of `Program.cs` with the demonstration code: + + :::code language="csharp" source="snippets/PointExtensions/Program.cs" id="TraditionalExtensionMethods"::: + +1. Run the sample application and examine the output. + +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. + +## Add static properties with extension members + +First, examine this code in the sample: + +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="StaticPropertySubstitute"::: + +Many apps that use 2D geometry use the concept of an `Origin`, which is the same value as . 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. + +Create `NewExtensionsMembers.cs` to create extension members that solve this problem: + +```csharp +using System.Drawing; +using System.Numerics; + +namespace ExtensionMembers; + +public static class PointExtensions +{ + extension (Point) + { + public static Point Origin => Point.Empty; + } +} +``` + +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. + +You can use this static property as though it were a member of the `Point` struct. + +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="StaticProperty"::: + +The `Point.Origin` property now appears as if it were part of the original `Point` type, providing a more intuitive API. + +## Implement arithmetic operators + +Next, examine the following code that performs arithmetic with points: + +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="PointArithmetic"::: + +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`: + +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="ArithmeticOperators"::: + +Extension members enable you to add operators directly to existing types. Now you can perform arithmetic operations using natural syntax: + +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="PointArithmeticWithOperators"::: + +The extension operators make point arithmetic as natural as working with built-in numeric types. + +## Add more operators + +You can also add extension operators for the discrete operations shown in the following code example: + +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="DiscreteXYOperators"::: + +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: + +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="TupleBasedXYOperators"::: + +The preceding operator enables elegant tuple-based operations: + +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="DiscreteXYOperators"::: + +Your extensions can include multiple overloaded operators, as long as the operands are distinct. + +## Migrate instance methods to extension members + +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. + +### Traditional extension methods + +The traditional approach uses the `this` parameter syntax: + +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="ExtensionMethods"::: + +Extension members use a different syntax but provide the same functionality. Add the following code to your new extension members class: + +:::code language="csharp" source="snippets/PointExtensions/NewExtensionsMembers.cs" id="TransformationMethods"::: + +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: + +```csharp + extension (Point) +``` + +So that it gives a name to the `Point` instance: + +```csharp + extension (Point point) +``` + +Now, the code compiles. You can call these new instance methods exactly as you accessed traditional extension methods: + +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="InstanceMethods"::: + +The key difference is syntax: extension members use `extension (Type variableName)` instead of `this Type variableName`. + +## Completed sample + +The final example shows the advantages when you combine static properties, operators, and instance methods to create comprehensive type extensions. + +Compare the extension member version: + +:::code language="csharp" source="snippets/PointExtensions/ExtensionMemberDemonstrations.cs" id="FinalScenarios"::: + +With the previous version: + +:::code language="csharp" source="snippets/PointExtensions/IncludedElements.cs" id="FinalScenarios"::: + +This example demonstrates how extension members create a cohesive API that feels like part of the original type. You can: + +- Use `Point.Origin` for a meaningful starting point +- Apply mathematical operators naturally (`point + offset`, `point * scale`) +- Chain transformations using both operators and methods +- Convert between related types (`ToVector()`) + +### Migration benefits + +When migrating from traditional extension methods to extension members, you gain: + +1. **Static properties**: Add constants and computed values to types. +1. **Operators**: Enable natural mathematical and logical operations. +1. **Unified syntax**: All extension logic uses the same `extension` declaration. +1. **Type-level extensions**: Extend the type itself, not just instances. + +Run the complete application to see both approaches side by side and observe how extension members provide a more integrated development experience. + +## Related content + +- [Extension methods (C# Programming Guide)](/dotnet/csharp/programming-guide/classes-and-structs/extension-methods) +- [What's new in C# 14](/dotnet/csharp/whats-new/csharp-14) +- [Operator overloading (C# reference)](/dotnet/csharp/language-reference/operators/operator-overloading) diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMemberDemonstrations.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMemberDemonstrations.cs new file mode 100644 index 0000000000000..6f7fb74cbd0d7 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMemberDemonstrations.cs @@ -0,0 +1,179 @@ +using System.Drawing; +using System.Numerics; +using ExtensionMembers; + +public static class ExtensionMemberDemonstrations +{ + public static void NewExtensionMembers() + { + Console.WriteLine("Point Extension Members Demonstration"); + Console.WriteLine("=====================================\n"); + + OriginAsStaticProperty(); + ArithmeticWithPoints(); + DiscreteArithmeticWithPoints(); + ExtensionMethods(); + MoreExamples(); + } + + static void OriginAsStaticProperty() + { + // + Console.WriteLine("1. Static Properties"); + Console.WriteLine("-------------------"); + + Point origin = Point.Origin; + Console.WriteLine($"Point.Origin: {origin}"); + Console.WriteLine($"Same as Point.Empty: {origin == Point.Empty}"); + Console.WriteLine(); + // + } + + static void ArithmeticWithPoints() + { + // + Console.WriteLine("2. Arithmetic Operators (Point + Point, Point - Point)"); + Console.WriteLine("-----------------------------------------------------"); + + Point p1 = new Point(5, 3); + Point p2 = new Point(2, 7); + + Console.WriteLine($"Point 1: {p1}"); + Console.WriteLine($"Point 2: {p2}"); + Console.WriteLine($"Addition (p1 + p2): {p1 + p2}"); + Console.WriteLine($"Subtraction (p1 - p2): {p1 - p2}"); + Console.WriteLine($"Subtraction (p2 - p1): {p2 - p1}"); + Console.WriteLine(); + // + } + + static void DiscreteArithmeticWithPoints() + { + // + Console.WriteLine("3. Discrete Operators using tuples (Point with (int, int))"); + Console.WriteLine("------------------------------------------"); + + Point point = new Point(10, 8); + var offset = (3, -2); + var scale = (2, 3); + var divisor = (2, 4); + + Console.WriteLine($"Original point: {point}"); + Console.WriteLine($"Offset tuple: {offset}"); + Console.WriteLine($"Scale tuple: {scale}"); + Console.WriteLine($"Divisor tuple: {divisor}"); + Console.WriteLine(); + + Console.WriteLine($"point + offset: {point + offset}"); + Console.WriteLine($"point - offset: {point - offset}"); + Console.WriteLine($"point * scale: {point * scale}"); + Console.WriteLine($"point / divisor: {point / divisor}"); + Console.WriteLine(); + // + } + + static void ExtensionMethods() + { + // + Console.WriteLine("4. Instance Methods"); + Console.WriteLine("------------------"); + + // ToVector demonstration + Point vectorPoint = new Point(12, 16); + Vector2 vector = vectorPoint.ToVector(); + Console.WriteLine($"Point {vectorPoint} as Vector2: {vector}"); + Console.WriteLine(); + + // Translate demonstration + Point translatePoint = new Point(5, 5); + Console.WriteLine($"Before Translate: {translatePoint}"); + translatePoint.Translate(3, -2); + Console.WriteLine($"After Translate(3, -2): {translatePoint}"); + Console.WriteLine(); + + // Scale demonstration + Point scalePoint = new Point(4, 6); + Console.WriteLine($"Before Scale: {scalePoint}"); + scalePoint.Scale(2, 3); + Console.WriteLine($"After Scale(2, 3): {scalePoint}"); + Console.WriteLine(); + + // Rotate demonstration + Point rotatePoint1 = new Point(10, 0); + Console.WriteLine($"Before Rotate: {rotatePoint1}"); + rotatePoint1.Rotate(90); + Console.WriteLine($"After Rotate(90°): {rotatePoint1}"); + + Point rotatePoint2 = new Point(5, 5); + Console.WriteLine($"Before Rotate: {rotatePoint2}"); + rotatePoint2.Rotate(45); + Console.WriteLine($"After Rotate(45°): {rotatePoint2}"); + + Point rotatePoint3 = new Point(3, 4); + Console.WriteLine($"Before Rotate: {rotatePoint3}"); + rotatePoint3.Rotate(180); + Console.WriteLine($"After Rotate(180°): {rotatePoint3}"); + Console.WriteLine(); + // + } + + static void MoreExamples() + { + // + Console.WriteLine("5. Complex Scenarios"); + Console.WriteLine("-------------------"); + + // Combining operators and methods + Console.WriteLine("Scenario 1: Building a rectangle using operators"); + Point topLeft = Point.Origin; + Point bottomRight = topLeft + (10, 8); + Point topRight = new Point(bottomRight.X, topLeft.Y); + Point bottomLeft = new Point(topLeft.X, bottomRight.Y); + + Console.WriteLine($"Rectangle corners:"); + Console.WriteLine($" Top-Left: {topLeft}"); + Console.WriteLine($" Top-Right: {topRight}"); + Console.WriteLine($" Bottom-Left: {bottomLeft}"); + Console.WriteLine($" Bottom-Right: {bottomRight}"); + Console.WriteLine(); + + // Transformation chain + Console.WriteLine("Scenario 2: Transformation chain"); + Point transformPoint = new Point(2, 3); + Console.WriteLine($"Starting point: {transformPoint}"); + + // Scale up + transformPoint.Scale(3, 2); + Console.WriteLine($"After scaling by (3, 2): {transformPoint}"); + + // Translate + transformPoint = transformPoint + (5, -3); + Console.WriteLine($"After translating by (5, -3): {transformPoint}"); + + // Rotate + transformPoint.Rotate(45); + Console.WriteLine($"After rotating 45°: {transformPoint}"); + + // Convert to vector + Vector2 finalVector = transformPoint.ToVector(); + Console.WriteLine($"Final result as Vector2: {finalVector}"); + Console.WriteLine(); + + // Distance calculation using operators + Console.WriteLine("Scenario 3: Distance calculation"); + Point point1 = new Point(1, 1); + Point point2 = new Point(4, 5); + Point difference = point2 - point1; + Vector2 diffVector = difference.ToVector(); + float distance = diffVector.Length(); + + Console.WriteLine($"Point 1: {point1}"); + Console.WriteLine($"Point 2: {point2}"); + Console.WriteLine($"Difference: {difference}"); + Console.WriteLine($"Distance: {distance:F2}"); + Console.WriteLine(); + + Console.WriteLine("Demonstration complete!"); + // + } +} \ No newline at end of file diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethods.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethods.cs new file mode 100644 index 0000000000000..d1ab573e9e0a0 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethods.cs @@ -0,0 +1,33 @@ +using System.Drawing; +using System.Numerics; + +namespace ExtensionMethods; + +public static class PointExtensions +{ + public static Vector2 ToVector(this Point point) => + new Vector2(point.X, point.Y); + + public static void Translate(this Point point, int xDist, int yDist) + { + point.X += xDist; + point.Y += yDist; + } + + public static void Scale(this Point point, int xScale, int yScale) + { + point.X *= xScale; + point.Y *= yScale; + } + + public static void Rotate(this Point point, int angleInDegrees) + { + double theta = ((double)angleInDegrees * Math.PI) / 180.0; + double sinTheta = Math.Sin(theta); + double cosTheta = Math.Cos(theta); + double newX = (double)point.X * cosTheta - (double)point.Y * sinTheta; + double newY = (double)point.X * sinTheta + (double)point.Y * cosTheta; + point.X = (int)newX; + point.Y = (int)newY; + } +} diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethodsDemonstrations.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethodsDemonstrations.cs new file mode 100644 index 0000000000000..7533c205853d8 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/ExtensionMethodsDemonstrations.cs @@ -0,0 +1,167 @@ +using System.Drawing; +using System.Numerics; +using ExtensionMethods; + +public static class ExtensionMethodsDemonstrations +{ + public static void TraditionalExtensionMethods() + { + OriginAsADataElement(); + ArithmeticWithPoints(); + DiscreteArithmeticWithPoints(); + ExtensionMethodsThis(); + MoreExamples(); + } + + static void OriginAsADataElement() + { + // Inline implementation since Point.Origin doesn't exist in ExtensionMethods + Point origin = Point.Empty; // Equivalent to Point.Origin + Console.WriteLine($"Point.Origin (inline): {origin}"); + Console.WriteLine($"Same as Point.Empty: {origin == Point.Empty}"); + Console.WriteLine(); + } + + static void ArithmeticWithPoints() + { + Point p1 = new Point(5, 3); + Point p2 = new Point(2, 7); + + Console.WriteLine($"Point 1: {p1}"); + Console.WriteLine($"Point 2: {p2}"); + + // Inline implementation since + and - operators don't exist in ExtensionMethods + Point addition = new Point(p1.X + p2.X, p1.Y + p2.Y); + Point subtraction1 = new Point(p1.X - p2.X, p1.Y - p2.Y); + Point subtraction2 = new Point(p2.X - p1.X, p2.Y - p1.Y); + + Console.WriteLine($"Addition (p1 + p2): {addition}"); + Console.WriteLine($"Subtraction (p1 - p2): {subtraction1}"); + Console.WriteLine($"Subtraction (p2 - p1): {subtraction2}"); + Console.WriteLine(); + } + + static void DiscreteArithmeticWithPoints() + { + Point point = new Point(10, 8); + int offsetX = 3; + int offsetY = -2; + int scaleX = 2; + int scaleY = 3; + int divisorX = 2; + int divisorY = 4; + + Console.WriteLine($"Original point: {point}"); + Console.WriteLine($"Offset: ({offsetX}, {offsetY})"); + Console.WriteLine($"Scale: ({scaleX}, {scaleY})"); + Console.WriteLine($"Divisor: ({divisorX}, {divisorY})"); + Console.WriteLine(); + + // Inline implementations since tuple operators don't exist in ExtensionMethods + Point addedOffset = new Point(point.X + offsetX, point.Y + offsetY); + Point subtractedOffset = new Point(point.X - offsetX, point.Y - offsetY); + Point scaledPoint = new Point(point.X * scaleX, point.Y * scaleY); + Point dividedPoint = new Point(point.X / divisorX, point.Y / divisorY); + + Console.WriteLine($"point + offset: {addedOffset}"); + Console.WriteLine($"point - offset: {subtractedOffset}"); + Console.WriteLine($"point * scale: {scaledPoint}"); + Console.WriteLine($"point / divisor: {dividedPoint}"); + Console.WriteLine(); + } + + static void ExtensionMethodsThis() + { + // ToVector demonstration - using extension method + Point vectorPoint = new Point(12, 16); + Vector2 vector = vectorPoint.ToVector(); + Console.WriteLine($"Point {vectorPoint} as Vector2: {vector}"); + Console.WriteLine(); + + // Translate demonstration - using extension method + Point translatePoint = new Point(5, 5); + Console.WriteLine($"Before Translate: {translatePoint}"); + translatePoint.Translate(3, -2); + Console.WriteLine($"After Translate(3, -2): {translatePoint}"); + Console.WriteLine(); + + // Scale demonstration - using extension method + Point scalePoint = new Point(4, 6); + Console.WriteLine($"Before Scale: {scalePoint}"); + scalePoint.Scale(2, 3); + Console.WriteLine($"After Scale(2, 3): {scalePoint}"); + Console.WriteLine(); + + // Rotate demonstration - using extension method + Point rotatePoint1 = new Point(10, 0); + Console.WriteLine($"Before Rotate: {rotatePoint1}"); + rotatePoint1.Rotate(90); + Console.WriteLine($"After Rotate(90°): {rotatePoint1}"); + + Point rotatePoint2 = new Point(5, 5); + Console.WriteLine($"Before Rotate: {rotatePoint2}"); + rotatePoint2.Rotate(45); + Console.WriteLine($"After Rotate(45°): {rotatePoint2}"); + + Point rotatePoint3 = new Point(3, 4); + Console.WriteLine($"Before Rotate: {rotatePoint3}"); + rotatePoint3.Rotate(180); + Console.WriteLine($"After Rotate(180°): {rotatePoint3}"); + Console.WriteLine(); + } + + static void MoreExamples() + { + // Combining operators and methods + Console.WriteLine("Scenario 1: Building a rectangle using inline operators"); + Point topLeft = Point.Empty; // Inline equivalent of Point.Origin + Point bottomRight = new Point(topLeft.X + 10, topLeft.Y + 8); // Inline addition + Point topRight = new Point(bottomRight.X, topLeft.Y); + Point bottomLeft = new Point(topLeft.X, bottomRight.Y); + + Console.WriteLine($"Rectangle corners:"); + Console.WriteLine($" Top-Left: {topLeft}"); + Console.WriteLine($" Top-Right: {topRight}"); + Console.WriteLine($" Bottom-Left: {bottomLeft}"); + Console.WriteLine($" Bottom-Right: {bottomRight}"); + Console.WriteLine(); + + // Transformation chain + Console.WriteLine("Scenario 2: Transformation chain (mixed methods)"); + Point transformPoint = new Point(2, 3); + Console.WriteLine($"Starting point: {transformPoint}"); + + // Scale up - using extension method + transformPoint.Scale(3, 2); + Console.WriteLine($"After scaling by (3, 2): {transformPoint}"); + + // Translate - using inline addition + transformPoint = new Point(transformPoint.X + 5, transformPoint.Y + (-3)); + Console.WriteLine($"After translating by (5, -3): {transformPoint}"); + + // Rotate - using extension method + transformPoint.Rotate(45); + Console.WriteLine($"After rotating 45�: {transformPoint}"); + + // Convert to vector - using extension method + Vector2 finalVector = transformPoint.ToVector(); + Console.WriteLine($"Final result as Vector2: {finalVector}"); + Console.WriteLine(); + + // Distance calculation using inline operators and extension methods + Console.WriteLine("Scenario 3: Distance calculation (mixed methods)"); + Point point1 = new Point(1, 1); + Point point2 = new Point(4, 5); + Point difference = new Point(point2.X - point1.X, point2.Y - point1.Y); // Inline subtraction + Vector2 diffVector = difference.ToVector(); // Extension method + float distance = diffVector.Length(); + + Console.WriteLine($"Point 1: {point1}"); + Console.WriteLine($"Point 2: {point2}"); + Console.WriteLine($"Difference: {difference}"); + Console.WriteLine($"Distance: {distance:F2}"); + Console.WriteLine(); + + Console.WriteLine("Traditional extension methods demonstration complete!"); + } +} \ No newline at end of file diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/IncludedElements.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/IncludedElements.cs new file mode 100644 index 0000000000000..03d1f4b37ccc8 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/IncludedElements.cs @@ -0,0 +1,208 @@ +using System.Drawing; +using System.Numerics; + +namespace IncludedElements; + +public static class PointExtensions +{ + // + public static Vector2 ToVector(this Point point) => + new Vector2(point.X, point.Y); + + public static void Translate(this Point point, int xDist, int yDist) + { + point.X += xDist; + point.Y += yDist; + } + + public static void Scale(this Point point, int xScale, int yScale) + { + point.X *= xScale; + point.Y *= yScale; + } + + public static void Rotate(this Point point, int angleInDegress) + { + double theta = ((double)angleInDegress * Math.PI) / 180.0; + double sinTheta = Math.Sin(theta); + double cosTheta = Math.Cos(theta); + double newX = (double)point.X * cosTheta - (double)point.Y * sinTheta; + double newY = (double)point.X * sinTheta + (double)point.Y * cosTheta; + point.X = (int)newX; + point.Y = (int)newY; + } + // +} + + +public static class ExtensionMethodsDemonstrations +{ + public static void TraditionalExtensionMethods() + { + OriginAsADataElement(); + ArithmeticWithPoints(); + DiscreteArithmeticWithPoints(); + ExtensionMethodsThis(); + MoreExamples(); + } + + static void OriginAsADataElement() + { + // + // Inline implementation since Point.Origin doesn't exist in ExtensionMethods + Point origin = Point.Empty; // Equivalent to Point.Origin + Console.WriteLine($"Point.Origin (inline): {origin}"); + Console.WriteLine($"Same as Point.Empty: {origin == Point.Empty}"); + Console.WriteLine(); + // + } + + static void ArithmeticWithPoints() + { + // + Point p1 = new Point(5, 3); + Point p2 = new Point(2, 7); + + Console.WriteLine($"Point 1: {p1}"); + Console.WriteLine($"Point 2: {p2}"); + + // Inline implementation since + and - operators don't exist in ExtensionMethods + Point addition = new Point(p1.X + p2.X, p1.Y + p2.Y); + Point subtraction1 = new Point(p1.X - p2.X, p1.Y - p2.Y); + Point subtraction2 = new Point(p2.X - p1.X, p2.Y - p1.Y); + + Console.WriteLine($"Addition (p1 + p2): {addition}"); + Console.WriteLine($"Subtraction (p1 - p2): {subtraction1}"); + Console.WriteLine($"Subtraction (p2 - p1): {subtraction2}"); + Console.WriteLine(); + // + } + + static void DiscreteArithmeticWithPoints() + { + // + Point point = new Point(10, 8); + int offsetX = 3; + int offsetY = -2; + int scaleX = 2; + int scaleY = 3; + int divisorX = 2; + int divisorY = 4; + + Console.WriteLine($"Original point: {point}"); + Console.WriteLine($"Offset: ({offsetX}, {offsetY})"); + Console.WriteLine($"Scale: ({scaleX}, {scaleY})"); + Console.WriteLine($"Divisor: ({divisorX}, {divisorY})"); + Console.WriteLine(); + + // Inline implementations since tuple operators don't exist in ExtensionMethods + Point addedOffset = new Point(point.X + offsetX, point.Y + offsetY); + Point subtractedOffset = new Point(point.X - offsetX, point.Y - offsetY); + Point scaledPoint = new Point(point.X * scaleX, point.Y * scaleY); + Point dividedPoint = new Point(point.X / divisorX, point.Y / divisorY); + + Console.WriteLine($"point + offset: {addedOffset}"); + Console.WriteLine($"point - offset: {subtractedOffset}"); + Console.WriteLine($"point * scale: {scaledPoint}"); + Console.WriteLine($"point / divisor: {dividedPoint}"); + Console.WriteLine(); + // + } + + static void ExtensionMethodsThis() + { + // ToVector demonstration - using extension method + Point vectorPoint = new Point(12, 16); + Vector2 vector = vectorPoint.ToVector(); + Console.WriteLine($"Point {vectorPoint} as Vector2: {vector}"); + Console.WriteLine(); + + // Translate demonstration - using extension method + Point translatePoint = new Point(5, 5); + Console.WriteLine($"Before Translate: {translatePoint}"); + translatePoint.Translate(3, -2); + Console.WriteLine($"After Translate(3, -2): {translatePoint}"); + Console.WriteLine(); + + // Scale demonstration - using extension method + Point scalePoint = new Point(4, 6); + Console.WriteLine($"Before Scale: {scalePoint}"); + scalePoint.Scale(2, 3); + Console.WriteLine($"After Scale(2, 3): {scalePoint}"); + Console.WriteLine(); + + // Rotate demonstration - using extension method + Point rotatePoint1 = new Point(10, 0); + Console.WriteLine($"Before Rotate: {rotatePoint1}"); + rotatePoint1.Rotate(90); + Console.WriteLine($"After Rotate(90°): {rotatePoint1}"); + + Point rotatePoint2 = new Point(5, 5); + Console.WriteLine($"Before Rotate: {rotatePoint2}"); + rotatePoint2.Rotate(45); + Console.WriteLine($"After Rotate(45°): {rotatePoint2}"); + + Point rotatePoint3 = new Point(3, 4); + Console.WriteLine($"Before Rotate: {rotatePoint3}"); + rotatePoint3.Rotate(180); + Console.WriteLine($"After Rotate(180°): {rotatePoint3}"); + Console.WriteLine(); + } + + static void MoreExamples() + { + // + // Combining operators and methods + Console.WriteLine("Scenario 1: Building a rectangle using inline operators"); + Point topLeft = Point.Empty; // Inline equivalent of Point.Origin + Point bottomRight = new Point(topLeft.X + 10, topLeft.Y + 8); // Inline addition + Point topRight = new Point(bottomRight.X, topLeft.Y); + Point bottomLeft = new Point(topLeft.X, bottomRight.Y); + + Console.WriteLine($"Rectangle corners:"); + Console.WriteLine($" Top-Left: {topLeft}"); + Console.WriteLine($" Top-Right: {topRight}"); + Console.WriteLine($" Bottom-Left: {bottomLeft}"); + Console.WriteLine($" Bottom-Right: {bottomRight}"); + Console.WriteLine(); + + // Transformation chain + Console.WriteLine("Scenario 2: Transformation chain (mixed methods)"); + Point transformPoint = new Point(2, 3); + Console.WriteLine($"Starting point: {transformPoint}"); + + // Scale up - using extension method + transformPoint.Scale(3, 2); + Console.WriteLine($"After scaling by (3, 2): {transformPoint}"); + + // Translate - using inline addition + transformPoint = new Point(transformPoint.X + 5, transformPoint.Y + (-3)); + Console.WriteLine($"After translating by (5, -3): {transformPoint}"); + + // Rotate - using extension method + transformPoint.Rotate(45); + Console.WriteLine($"After rotating 45�: {transformPoint}"); + + // Convert to vector - using extension method + Vector2 finalVector = transformPoint.ToVector(); + Console.WriteLine($"Final result as Vector2: {finalVector}"); + Console.WriteLine(); + + // Distance calculation using inline operators and extension methods + Console.WriteLine("Scenario 3: Distance calculation (mixed methods)"); + Point point1 = new Point(1, 1); + Point point2 = new Point(4, 5); + Point difference = new Point(point2.X - point1.X, point2.Y - point1.Y); // Inline subtraction + Vector2 diffVector = difference.ToVector(); // Extension method + float distance = diffVector.Length(); + + Console.WriteLine($"Point 1: {point1}"); + Console.WriteLine($"Point 2: {point2}"); + Console.WriteLine($"Difference: {difference}"); + Console.WriteLine($"Distance: {distance:F2}"); + Console.WriteLine(); + + Console.WriteLine("Traditional extension methods demonstration complete!"); + // + } +} diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/NewExtensionsMembers.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/NewExtensionsMembers.cs new file mode 100644 index 0000000000000..b97e26ba345c9 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/NewExtensionsMembers.cs @@ -0,0 +1,60 @@ +using System.Drawing; +using System.Numerics; + +namespace ExtensionMembers; + +public static class PointExtensions +{ + extension(Point point) + { + public static Point Origin => Point.Empty; + + // + public static Point operator +(Point left, Point right) => + new Point(left.X + right.X, left.Y + right.Y); + + public static Point operator -(Point left, Point right) => + new Point(left.X - right.X, left.Y - right.Y); + // + + // + public static Point operator *(Point left, (int dx, int dy) scale) => + new Point(left.X * scale.dx, left.Y * scale.dy); + public static Point operator /(Point left, (int dx, int dy) scale) => + new Point(left.X / scale.dx, left.Y / scale.dy); + public static Point operator +(Point left, (int dx, int dy) scale) => + new Point(left.X + scale.dx, left.Y + scale.dy); + public static Point operator -(Point left, (int dx, int dy) scale) => + new Point(left.X - scale.dx, left.Y - scale.dy); + // + + // + public Vector2 ToVector() => + new Vector2(point.X, point.Y); + + public void Translate(int xDist, int yDist) + { + point.X += xDist; + point.Y += yDist; + } + + public void Scale(int xScale, int yScale) + { + point.X *= xScale; + point.Y *= yScale; + } + + public void Rotate(int angleInDegrees) + { + double theta = ((double)angleInDegrees * Math.PI) / 180.0; + double sinTheta = Math.Sin(theta); + double cosTheta = Math.Cos(theta); + double newX = (double)point.X * cosTheta - (double)point.Y * sinTheta; + double newY = (double)point.X * sinTheta + (double)point.Y * cosTheta; + point.X = (int)newX; + point.Y = (int)newY; + } + // + + } +} diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/PointExtensions.csproj b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/PointExtensions.csproj new file mode 100644 index 0000000000000..ed9781c223ab9 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/PointExtensions.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/docs/csharp/whats-new/tutorials/snippets/PointExtensions/Program.cs b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/Program.cs new file mode 100644 index 0000000000000..98a95def8cab5 --- /dev/null +++ b/docs/csharp/whats-new/tutorials/snippets/PointExtensions/Program.cs @@ -0,0 +1,7 @@ +// +ExtensionMethodsDemonstrations.TraditionalExtensionMethods(); +// + +Console.WriteLine("\n\n"); + +ExtensionMemberDemonstrations.NewExtensionMembers();