Skip to content

Commit edbc583

Browse files
committed
Finish edits on extension members
1 parent 5f99255 commit edbc583

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

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

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "extension member declarations"
2+
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."
44
ms.date: 04/17/2025
55
f1_keywords:
@@ -8,13 +8,13 @@ f1_keywords:
88
---
99
# Extension declaration (C# Reference)
1010

11-
Beginning with C# 14, top level, non-generic `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, non-generic static class.
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.
1212

1313
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.
1414

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

17-
The `extension` defines the receiver: `sequence`, which is the an `IEnumerable<int>`. The receiver type can be non-generic, an open generic, or a closed generic type. The name `sequence` is in scope in every instance member declared in that extension. The extension method and property both access `sequence`.
17+
The `extension` defines the receiver: `sequence`, which is an `IEnumerable<int>`. The receiver type can be nongeneric, an open generic, or a closed generic type. The name `sequence` is in scope in every instance member declared in that extension. The extension method and property both access `sequence`.
1818

1919
Any of the extension members can be accessed as though they were members of the receiver type:
2020

@@ -24,7 +24,7 @@ You can declare any number of members in a single container, as long as they sha
2424

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

27-
Static extensions can be called as though they are static members of the receiver type:
27+
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

@@ -41,10 +41,23 @@ The `Add` method can be called from any other method as though it was a member o
4141

4242
Both forms of extension methods generate the same intermediate language (IL). Callers can't make a distinction between them. In fact, you can convert existing extension methods to the new member syntax without a breaking change. The formats are both binary and source compatible.
4343

44-
TODO: Add Generic extension members.
45-
- Type parameter on receiver
46-
- Type parameter that's not the receiver on a member
47-
- Type parameter on receiver + additional
44+
## Generic extension blocks
45+
46+
Where you specify the type parameters for an extension member declared in an extension block depends on where that type parameter is required:
47+
48+
- You add the type parameter to the `extension` declaration when the type parameter is used in the receiver.
49+
- You add the type parameter to the member declaration when the type is distinct from any type parameter specified on the receiver.
50+
- You can't specify the same type parameter in both locations.
51+
52+
The following example shows an extension block for `IEnumerable<T>` where two of the extension members require a second type parameter:
53+
54+
:::code language="csharp" source="./snippets/extensions.cs" id="GenericExtension":::
55+
56+
The members `Append` and `Prepend` specify the *extra* type parameter for the conversion. None of the members repeat the type parameter for the receiver.
57+
58+
The equivalent extension method declarations demonstrate how those type parameters are encoded:
59+
60+
:::code language="csharp" source="./snippets/ExtensionMethods.cs" id="GenericExtensionMethods":::
4861

4962
## See also
5063

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

+33
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,36 @@ public static void BasicExample()
2626
}
2727
}
2828

29+
// <GenericExtensionMethod>
30+
public static class GenericExtensions
31+
{
32+
public static IEnumerable<T> Spread<T>(this IEnumerable<T> source, int start, int count)
33+
=> source.Skip(start).Take(count);
34+
35+
public static IEnumerable<T1> Append<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
36+
{
37+
foreach (T1 item in source)
38+
{
39+
yield return item;
40+
}
41+
foreach (T2 item in second)
42+
{
43+
yield return Converter(item);
44+
}
45+
}
46+
47+
public static IEnumerable<T1> Prepend<T1, TArg>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
48+
{
49+
foreach (T2 item in second)
50+
{
51+
yield return Converter(item);
52+
}
53+
foreach (T1 item in source)
54+
{
55+
yield return item;
56+
}
57+
}
58+
}
59+
// </GenericExtensionMethod>
60+
61+

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

+35
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,41 @@ public static IEnumerable<int> Generate(int low, int count, int increment)
5858
// </StaticExtensions>
5959
}
6060

61+
// <GenericExtension>
62+
public static class GenericExtensions
63+
{
64+
extension<TReceiver>(IEnumerable<TReceiver> source)
65+
{
66+
public IEnumerable<TReceiver> Spread(int start, int count)
67+
=> source.Skip(start).Take(count);
68+
69+
public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
70+
{
71+
foreach(TReceiver item in source)
72+
{
73+
yield return item;
74+
}
75+
foreach (TArg item in second)
76+
{
77+
yield return Converter(item);
78+
}
79+
}
80+
81+
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
82+
{
83+
foreach (TArg item in second)
84+
{
85+
yield return Converter(item);
86+
}
87+
foreach (TReceiver item in source)
88+
{
89+
yield return item;
90+
}
91+
}
92+
}
93+
}
94+
// </GenericExtension>
95+
6196
public static class ExtensionExamples
6297
{
6398
public static void BasicExample()

0 commit comments

Comments
 (0)