Skip to content

Commit 87c026d

Browse files
BillWagnerCopilotgewarren
authored
Replace extension method with member (dotnet#50113)
* Replace extension method with member Where samples use `this` extension methods, except where explaining the `this` syntax, convert to the C# 14 extension member syntax, * Fix build issues * Add updates in the advanced-topics section * Add other C# folders This adds all the folders *except* tutorials and LINQ. (Those will be in separate PRs) * Add the fundamentals folder * Apply suggestions from code review Co-authored-by: Copilot <[email protected]> * Apply suggestions from code review Co-authored-by: Genevieve Warren <[email protected]> * Respond to feedback. --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Genevieve Warren <[email protected]>
1 parent b3c7d87 commit 87c026d

File tree

24 files changed

+122
-323
lines changed

24 files changed

+122
-323
lines changed

docs/csharp/advanced-topics/interface-implementation/mixins-with-default-interface-methods.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Create mixin types using default interface methods
33
description: Using default interface members you can extend interfaces with optional default implementations for implementors.
4-
ms.date: 07/31/2024
4+
ms.date: 11/24/2025
55
---
66
# Tutorial: Mix functionality in when creating classes using interfaces with default interface methods
77

@@ -19,13 +19,13 @@ In this tutorial, you learn how to:
1919

2020
You need to set up your machine to run .NET, including the C# compiler. The C# compiler is available with [Visual Studio 2022](https://visualstudio.microsoft.com/downloads), or the [.NET SDK](https://dotnet.microsoft.com/download/dotnet).
2121

22-
## Limitations of extension methods
22+
## Limitations of extensions
2323

24-
One way you can implement behavior that appears as part of an interface is to define [extension methods](../../programming-guide/classes-and-structs/extension-methods.md) that provide the default behavior. Interfaces declare a minimum set of members while providing a greater surface area for any class that implements that interface. For example, the extension methods in <xref:System.Linq.Enumerable> provide the implementation for any sequence to be the source of a LINQ query.
24+
One way you can implement behavior that appears as part of an interface is to define [extension members](../../programming-guide/classes-and-structs/extension-methods.md) that provide the default behavior. Interfaces declare a minimum set of members while providing a greater surface area for any class that implements that interface. For example, the extension members in <xref:System.Linq.Enumerable> provide the implementation for any sequence to be the source of a LINQ query.
2525

26-
Extension methods are resolved at compile time, using the declared type of the variable. Classes that implement the interface can provide a better implementation for any extension method. Variable declarations must match the implementing type to enable the compiler to choose that implementation. When the compile-time type matches the interface, method calls resolve to the extension method. Another concern with extension methods is that those methods are accessible wherever the class containing the extension methods is accessible. Classes can't declare if they should or shouldn't provide features declared in extension methods.
26+
Extension members are resolved at compile time, using the declared type of the variable. Classes that implement the interface can provide a better implementation for any extension member. Variable declarations must match the implementing type to enable the compiler to choose that implementation. When the compile-time type matches the interface, method calls resolve to the extension member. Another concern with extension members is that those members are accessible wherever the class containing the extension member is accessible. Classes can't declare if they should or shouldn't provide features declared in extension members.
2727

28-
You can declare the default implementations as interface methods. Then, every class automatically uses the default implementation. Any class that can provide a better implementation can override the interface method definition with a better algorithm. In one sense, this technique sounds similar to how you could use [extension methods](../../programming-guide/classes-and-structs/extension-methods.md).
28+
You can declare the default implementations as interface methods. Then, every class automatically uses the default implementation. Any class that can provide a better implementation can override the interface method definition with a better algorithm. In one sense, this technique sounds similar to how you could use [extension members](../../programming-guide/classes-and-structs/extension-methods.md).
2929

3030
In this article, you learn how default interface implementations enable new scenarios.
3131

@@ -38,7 +38,7 @@ Consider a home automation application. You probably have many different types o
3838

3939
Some of these extended capabilities could be emulated in devices that support the minimal set. That indicates providing a default implementation. For those devices that have more capabilities built in, the device software would use the native capabilities. For other lights, they could choose to implement the interface and use the default implementation.
4040

41-
Default interface members provide a better solution for this scenario than extension methods. Class authors can control which interfaces they choose to implement. Those interfaces they choose are available as methods. In addition, because default interface methods are virtual by default, the method dispatch always chooses the implementation in the class.
41+
Default interface members provide a better solution for this scenario than extension members. Class authors can control which interfaces they choose to implement. Those interfaces they choose are available as methods. In addition, because default interface methods are virtual by default, the method dispatch always chooses the implementation in the class.
4242

4343
Let's create the code to demonstrate these differences.
4444

docs/csharp/advanced-topics/interface-implementation/snippets/mixins-with-default-interface-methods/mixins-with-interfaces.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<Nullable>enable</Nullable>
77
<ImplicitUsings>enable</ImplicitUsings>
88
<RootNamespace>mixins_with_interfaces</RootNamespace>

docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruct-extension1.cs

Lines changed: 69 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,84 +4,87 @@
44

55
public static class ReflectionExtensions
66
{
7-
public static void Deconstruct(this PropertyInfo p, out bool isStatic,
8-
out bool isReadOnly, out bool isIndexed,
9-
out Type propertyType)
7+
extension(PropertyInfo propertyInfo)
108
{
11-
var getter = p.GetMethod;
9+
public void Deconstruct(out bool isStatic,
10+
out bool isReadOnly, out bool isIndexed,
11+
out Type propertyType)
12+
{
13+
var getter = propertyInfo.GetMethod;
1214

13-
// Is the property read-only?
14-
isReadOnly = ! p.CanWrite;
15+
// Is the property read-only?
16+
isReadOnly = ! propertyInfo.CanWrite;
1517

16-
// Is the property instance or static?
17-
isStatic = getter.IsStatic;
18+
// Is the property instance or static?
19+
isStatic = getter.IsStatic;
1820

19-
// Is the property indexed?
20-
isIndexed = p.GetIndexParameters().Length > 0;
21+
// Is the property indexed?
22+
isIndexed = propertyInfo.GetIndexParameters().Length > 0;
2123

22-
// Get the property type.
23-
propertyType = p.PropertyType;
24-
}
24+
// Get the property type.
25+
propertyType = propertyInfo.PropertyType;
26+
}
2527

26-
public static void Deconstruct(this PropertyInfo p, out bool hasGetAndSet,
27-
out bool sameAccess, out string access,
28-
out string getAccess, out string setAccess)
29-
{
30-
hasGetAndSet = sameAccess = false;
31-
string getAccessTemp = null;
32-
string setAccessTemp = null;
28+
public void Deconstruct(out bool hasGetAndSet,
29+
out bool sameAccess, out string access,
30+
out string getAccess, out string setAccess)
31+
{
32+
hasGetAndSet = sameAccess = false;
33+
string getAccessTemp = null;
34+
string setAccessTemp = null;
3335

34-
MethodInfo getter = null;
35-
if (p.CanRead)
36-
getter = p.GetMethod;
36+
MethodInfo getter = null;
37+
if (propertyInfo.CanRead)
38+
getter = propertyInfo.GetMethod;
3739

38-
MethodInfo setter = null;
39-
if (p.CanWrite)
40-
setter = p.SetMethod;
40+
MethodInfo setter = null;
41+
if (propertyInfo.CanWrite)
42+
setter = propertyInfo.SetMethod;
4143

42-
if (setter != null && getter != null)
43-
hasGetAndSet = true;
44+
if (setter != null && getter != null)
45+
hasGetAndSet = true;
4446

45-
if (getter != null)
46-
{
47-
if (getter.IsPublic)
48-
getAccessTemp = "public";
49-
else if (getter.IsPrivate)
50-
getAccessTemp = "private";
51-
else if (getter.IsAssembly)
52-
getAccessTemp = "internal";
53-
else if (getter.IsFamily)
54-
getAccessTemp = "protected";
55-
else if (getter.IsFamilyOrAssembly)
56-
getAccessTemp = "protected internal";
57-
}
47+
if (getter != null)
48+
{
49+
if (getter.IsPublic)
50+
getAccessTemp = "public";
51+
else if (getter.IsPrivate)
52+
getAccessTemp = "private";
53+
else if (getter.IsAssembly)
54+
getAccessTemp = "internal";
55+
else if (getter.IsFamily)
56+
getAccessTemp = "protected";
57+
else if (getter.IsFamilyOrAssembly)
58+
getAccessTemp = "protected internal";
59+
}
5860

59-
if (setter != null)
60-
{
61-
if (setter.IsPublic)
62-
setAccessTemp = "public";
63-
else if (setter.IsPrivate)
64-
setAccessTemp = "private";
65-
else if (setter.IsAssembly)
66-
setAccessTemp = "internal";
67-
else if (setter.IsFamily)
68-
setAccessTemp = "protected";
69-
else if (setter.IsFamilyOrAssembly)
70-
setAccessTemp = "protected internal";
71-
}
61+
if (setter != null)
62+
{
63+
if (setter.IsPublic)
64+
setAccessTemp = "public";
65+
else if (setter.IsPrivate)
66+
setAccessTemp = "private";
67+
else if (setter.IsAssembly)
68+
setAccessTemp = "internal";
69+
else if (setter.IsFamily)
70+
setAccessTemp = "protected";
71+
else if (setter.IsFamilyOrAssembly)
72+
setAccessTemp = "protected internal";
73+
}
7274

73-
// Are the accessibility of the getter and setter the same?
74-
if (setAccessTemp == getAccessTemp)
75-
{
76-
sameAccess = true;
77-
access = getAccessTemp;
78-
getAccess = setAccess = String.Empty;
79-
}
80-
else
81-
{
82-
access = null;
83-
getAccess = getAccessTemp;
84-
setAccess = setAccessTemp;
75+
// Are the accessibility of the getter and setter the same?
76+
if (setAccessTemp == getAccessTemp)
77+
{
78+
sameAccess = true;
79+
access = getAccessTemp;
80+
getAccess = setAccess = String.Empty;
81+
}
82+
else
83+
{
84+
access = null;
85+
getAccess = getAccessTemp;
86+
setAccess = setAccessTemp;
87+
}
8588
}
8689
}
8790
}

docs/csharp/fundamentals/functional/snippets/deconstructing-tuples/deconstruction.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net9.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<Nullable>enable</Nullable>
77
<StartupObject>deconstruction.Program</StartupObject>
88
</PropertyGroup>

docs/csharp/how-to/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ There are several tips and tricks that are common C# developer practices:
1515

1616
- [Initialize objects using an object initializer](../programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer.md).
1717
- [Use operator overloading](../language-reference/operators/operator-overloading.md).
18-
- [Implement and call a custom extension method](../programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method.md).
19-
- [Create a new method for an `enum` type using extension methods](../programming-guide/classes-and-structs/how-to-create-a-new-method-for-an-enumeration.md).
18+
- [Implement and call a custom extension member](../programming-guide/classes-and-structs/how-to-implement-and-call-a-custom-extension-method.md).
19+
- [Create a new method for an `enum` type using extension member](../programming-guide/classes-and-structs/how-to-create-a-new-method-for-an-enumeration.md).
2020

2121
### Class, record, and struct members
2222

docs/csharp/language-reference/attributes/caller-information.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ The compiler injects the expression used for `condition` into the `message` argu
5858
Argument failed validation: <func is not null>
5959
```
6060

61-
This attribute enables you to write diagnostic utilities that provide more details. Developers can more quickly understand what changes are needed. You can also use the <xref:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute> to determine what expression was used as the receiver for extension methods. The following method samples a sequence at regular intervals. If the sequence has fewer elements than the frequency, it reports an error:
61+
This attribute enables you to write diagnostic utilities that provide more details. Developers can more quickly understand what changes are needed. You can also use the <xref:System.Runtime.CompilerServices.CallerArgumentExpressionAttribute> to determine what expression was used as the receiver for extension members. The following method samples a sequence at regular intervals. If the sequence has fewer elements than the frequency, it reports an error:
6262

6363
:::code language="csharp" source="./snippets/CallerInformation.cs" id="ExtensionMethod":::
6464

docs/csharp/language-reference/attributes/snippets/CallerInformation.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,19 @@ public static void ValidateArgument(string parameterName, bool condition, [Calle
5656
public static class Extensions
5757
{
5858
// <ExtensionMethod>
59-
public static IEnumerable<T> Sample<T>(this IEnumerable<T> sequence, int frequency,
60-
[CallerArgumentExpression(nameof(sequence))] string? message = null)
59+
extension<T>(IEnumerable<T> sequence)
6160
{
62-
if (sequence.Count() < frequency)
63-
throw new ArgumentException($"Expression doesn't have enough elements: {message}", nameof(sequence));
64-
int i = 0;
65-
foreach (T item in sequence)
61+
public IEnumerable<T> Sample(int frequency,
62+
[CallerArgumentExpression(nameof(sequence))] string? message = null)
6663
{
67-
if (i++ % frequency == 0)
68-
yield return item;
64+
if (sequence.Count() < frequency)
65+
throw new InvalidOperationException($"Expression doesn't have enough elements: {message}");
66+
int i = 0;
67+
foreach (T item in sequence)
68+
{
69+
if (i++ % frequency == 0)
70+
yield return item;
71+
}
6972
}
7073
}
7174
// </ExtensionMethod>

docs/csharp/language-reference/attributes/snippets/attributes.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net9.0</TargetFramework>
5+
<TargetFramework>net10.0</TargetFramework>
66
<Nullable>enable</Nullable>
77
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
88
<ImplicitUsings>enable</ImplicitUsings>

docs/csharp/language-reference/builtin-types/built-in-types.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Built-in types"
33
description: "Learn C# built-in value and reference types"
4-
ms.date: 03/07/2025
4+
ms.date: 11/24/2025
55
helpviewer_keywords:
66
- "types [C#], built-in"
77
- "built-in C# types"
@@ -62,7 +62,7 @@ The C# language includes specialized rules for the <xref:System.Span`1?displayPr
6262
- From `System.ReadOnlySpan<E>` to `System.ReadOnlySpan<U>`, when `E` has a covariance conversion or an identity conversion to `U`
6363
- From `string` to `System.ReadOnlySpan<char>`
6464

65-
The compiler never ignores any user defined conversion where an applicable *implicit span conversion* exists. Implicit span conversions can be applied to the first argument of [extension methods](../../programming-guide/classes-and-structs/extension-methods.md), the parameter with the `this` modifier. Implicit span conversions aren't considered for method group conversions.
65+
The compiler never ignores any user defined conversion where an applicable *implicit span conversion* exists. Implicit span conversions can be applied to receiver parameter of [extension members](../../programming-guide/classes-and-structs/extension-methods.md). The receiver parameter is specified by the [`extension`](../keywords/extension.md) keyword in an extension member. The receiver parameter is the first parameter of an extension method using the `this` modifier. Implicit span conversions aren't considered for method group conversions.
6666

6767
## See also
6868

docs/csharp/language-reference/builtin-types/enum.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: "Enumeration types"
33
description: "Learn about C# enumeration types that represent a choice or a combination of choices"
4-
ms.date: 12/13/2019
4+
ms.date: 11/24/2025
55
f1_keywords:
66
- "enum"
77
- "enum_CSharpKeyword"
@@ -10,7 +10,6 @@ helpviewer_keywords:
1010
- "enum type [C#]"
1111
- "enumeration type [C#]"
1212
- "bit flags [C#]"
13-
ms.assetid: bbeb9a0f-e9b3-41ab-b0a6-c41b1a08974c
1413
---
1514
# Enumeration types (C# reference)
1615

@@ -38,7 +37,7 @@ enum ErrorCode : ushort
3837
}
3938
```
4039

41-
You cannot define a method inside the definition of an enumeration type. To add functionality to an enumeration type, create an [extension method](../../programming-guide/classes-and-structs/extension-methods.md).
40+
You can't define a method inside the definition of an enumeration type. To add functionality to an enumeration type, create an [extension member](../../programming-guide/classes-and-structs/extension-methods.md).
4241

4342
The default value of an enumeration type `E` is the value produced by expression `(E)0`, even if zero doesn't have the corresponding enum member.
4443

@@ -50,7 +49,7 @@ C# allows implicit conversions from the literal value `0` to any enum type, and
5049

5150
In the preceding example, both `port1` and `port2` are assigned the value `0`, but `GpioPort` has no member with that value. The <xref:System.Enum.IsDefined%2A?displayProperty=nameWithType> method confirms these are invalid enum values.
5251

53-
This implicit conversion exists because the 0 bit pattern is the default for all struct types, including all enum types. However, it can introduce bugs in your code. To avoid these issues:
52+
This implicit conversion exists because the 0-bit pattern is the default for all struct types, including all enum types. However, it can introduce bugs in your code. To avoid these issues:
5453

5554
- You should almost always define a member with value `0` in your enums.
5655
- Use <xref:System.Enum.IsDefined%2A?displayProperty=nameWithType> to validate enum values when converting from numeric types.
@@ -62,7 +61,7 @@ You use an enumeration type to represent a choice from a set of mutually exclusi
6261

6362
If you want an enumeration type to represent a combination of choices, define enum members for those choices such that an individual choice is a bit field. That is, the associated values of those enum members should be the powers of two. Then, you can use the [bitwise logical operators `|` or `&`](../operators/bitwise-and-shift-operators.md#enumeration-logical-operators) to combine choices or intersect combinations of choices, respectively. To indicate that an enumeration type declares bit fields, apply the [Flags](xref:System.FlagsAttribute) attribute to it. As the following example shows, you can also include some typical combinations in the definition of an enumeration type.
6463

65-
[!code-csharp[enum flags](snippets/shared/EnumType.cs#Flags)]
64+
:::code language="csharp" source="snippets/shared/EnumType.cs" id="SnippetFlags":::
6665

6766
For more information and examples, see the <xref:System.FlagsAttribute?displayProperty=nameWithType> API reference page and the [Non-exclusive members and the Flags attribute](../../../fundamentals/runtime-libraries/system-enum.md#non-exclusive-members-and-the-flags-attribute) section of the <xref:System.Enum?displayProperty=nameWithType> API reference page.
6867

@@ -76,7 +75,7 @@ You can use `System.Enum` in a base class constraint (that is known as the [enum
7675

7776
For any enumeration type, there exist explicit conversions between the enumeration type and its underlying integral type. If you [cast](../operators/type-testing-and-cast.md#cast-expression) an enum value to its underlying type, the result is the associated integral value of an enum member.
7877

79-
[!code-csharp[enum conversions](snippets/shared/EnumType.cs#Conversions)]
78+
:::code language="csharp" source="snippets/shared/EnumType.cs" id="SnippetConversions":::
8079

8180
Use the <xref:System.Enum.IsDefined%2A?displayProperty=nameWithType> method to determine whether an enumeration type contains an enum member with the certain associated value.
8281

0 commit comments

Comments
 (0)