Skip to content

Commit 7c97d76

Browse files
BillWagnergewarren
andauthored
Add examples for XML on extension members (#50551)
* Add examples for XML on `extension`s Fixes #49956 Add examples and explanations for writing documentation on `extension` nodes. Include how the XML comments on the `extension` node for `<summary>`, `<param>` and `<typeparam>` are copied to all members of that extension block. * forgot the XML tag * Copilot style feedback. * Apply suggestions from code review Co-authored-by: Genevieve Warren <[email protected]> * Respond to feedback. --------- Co-authored-by: Genevieve Warren <[email protected]>
1 parent 7b833bd commit 7c97d76

File tree

5 files changed

+182
-19
lines changed

5 files changed

+182
-19
lines changed

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,37 @@
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: 09/17/2025
4+
ms.date: 12/11/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` 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.
11+
Starting 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

1313
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

15+
> [!NOTE]
16+
> All the examples in this article include XML comments for the members and the extension block. The node on the `extension` block describes the extended type and the receiver parameter. The C# compiler copies this node to the generated member for all members in the extension block. These examples demonstrate the preferred style for generating XML documentation for extension members.
17+
1518
:::code language="csharp" source="./snippets/extensions.cs" id="ExtensionMembers":::
1619

1720
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`.
1821

19-
Any of the extension members can be accessed as though they were members of the receiver type:
22+
You access any of the extension members as though they were members of the receiver type:
2023

2124
:::code language="csharp" source="./snippets/extensions.cs" id="UseExtensionMethod":::
2225

2326
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:
2427

2528
:::code language="csharp" source="./snippets/extensions.cs" id="StaticExtensions":::
2629

27-
Static extensions can be called as though they're static members of the receiver type:
30+
You call static extensions as though they're static members of the receiver type:
2831

2932
:::code language="csharp" source="./snippets/extensions.cs" id="UseStaticExtensions":::
3033

31-
Operators are called as though they're user defined operators on the type.
34+
You call operators as though they're user defined operators on the type.
3235

3336
> [!IMPORTANT]
3437
> 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.
@@ -37,18 +40,18 @@ The following example shows an extension method using the `this` modifier:
3740

3841
:::code language="csharp" source="./snippets/ExtensionMethods.cs" id="ExtensionMethod":::
3942

40-
The `Add` method can be called from any other method as though it was a member of the `IEnumerable<int>` interface:
43+
You can call the `Add` method from any other method as though it was a member of the `IEnumerable<int>` interface:
4144

4245
:::code language="csharp" source="./snippets/ExtensionMethods.cs" id="UseExtensionMethod":::
4346

4447
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.
4548

4649
## Generic extension blocks
4750

48-
Where you specify the type parameters for an extension member declared in an extension block depends on where that type parameter is required:
51+
Where you specify the type parameters for an extension member declared in an extension block depends on where you need the type parameter:
4952

50-
- You add the type parameter to the `extension` declaration when the type parameter is used in the receiver.
51-
- You add the type parameter to the member declaration when the type is distinct from any type parameter specified on the receiver.
53+
- Add the type parameter to the `extension` declaration when the type parameter is used in the receiver.
54+
- Add the type parameter to the member declaration when the type is distinct from any type parameter specified on the receiver.
5255
- You can't specify the same type parameter in both locations.
5356

5457
The following example shows an extension block for `IEnumerable<T>` where two of the extension members require a second type parameter:

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

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
namespace keywords;
22

33
// <ExtensionMembers>
4+
/// <summary>
5+
/// Contains extension members for numeric sequences.
6+
/// </summary>
47
public static class NumericSequences
58
{
9+
/// <summary>
10+
/// Defines extensions for integer sequences.
11+
/// </summary>
12+
/// <param name="sequence">The sequence used as a receiver.</param>
613
extension(IEnumerable<int> sequence)
714
{
15+
/// <summary>
16+
/// Adds a scalar value to each element in the sequence.
17+
/// </summary>
18+
/// <param name="operand">The amount to add.</param>
19+
/// <returns>
20+
/// A new sequence where each value contains the updated value.
21+
/// </returns>
822
public IEnumerable<int> AddValue(int operand)
923
{
1024
foreach (var item in sequence)
@@ -13,6 +27,12 @@ public IEnumerable<int> AddValue(int operand)
1327
}
1428
}
1529

30+
/// <summary>
31+
/// Gets the median value of the sequence.
32+
/// </summary>
33+
/// <remarks>
34+
/// This value is calculated when requested.
35+
/// </remarks>
1636
public int Median
1737
{
1838
get
@@ -35,6 +55,16 @@ public int Median
3555
}
3656
}
3757

58+
/// <summary>
59+
/// Concatenates two sequences of integers into a single sequence.
60+
/// </summary>
61+
/// <remarks>The resulting sequence enumerates all elements from <paramref name="left"/> in order,
62+
/// followed by all elements from <paramref name="right"/>. Enumeration is deferred and performed lazily as the
63+
/// returned sequence is iterated.</remarks>
64+
/// <param name="left">The first sequence of integers to concatenate. Cannot be null.</param>
65+
/// <param name="right">The second sequence of integers to concatenate. Cannot be null.</param>
66+
/// <returns>A sequence that contains the elements of the first sequence followed by the
67+
/// elements of the second sequence.</returns>
3868
public static IEnumerable<int> operator +(IEnumerable<int> left, IEnumerable<int> right)
3969
=> left.Concat(right);
4070
}
@@ -44,29 +74,82 @@ public int Median
4474
public static class NumericStaticExtensions
4575
{
4676
// <StaticExtensions>
77+
/// <summary>
78+
/// Provides static extensions for the <see cref="IEnumerable{Int32}"/> type.
79+
/// </summary>
4780
extension(IEnumerable<int>)
4881
{
4982
// Method:
83+
/// <summary>
84+
/// Generates a sequence of integers, starting from a specified value and incrementing by a given amount.
85+
/// </summary>
86+
/// <param name="low">The starting value of the sequence.</param>
87+
/// <param name="count">The number of integers to generate. Must be non-negative.</param>
88+
/// <param name="increment">The value by which to increment each subsequent integer in the sequence.</param>
89+
/// <returns>
90+
/// An enumerable collection of integers, beginning with the specified starting value and containing the
91+
/// specified number of elements, each incremented by the given amount.
92+
/// </returns>
5093
public static IEnumerable<int> Generate(int low, int count, int increment)
5194
{
5295
for (int i = 0; i < count; i++)
5396
yield return low + (i * increment);
5497
}
5598

5699
// Property:
100+
/// <summary>
101+
/// Gets an empty sequence of integers representing the identity element for sequence operations.
102+
/// </summary>
103+
/// <remarks>
104+
/// This property can be used as a neutral starting point when aggregating or composing
105+
/// sequences of integers. The returned sequence is always empty and does not allocate any storage.
106+
/// </remarks>
57107
public static IEnumerable<int> Identity => Enumerable.Empty<int>();
58108
}
59109
// </StaticExtensions>
60110
}
61111

62112
// <GenericExtension>
113+
/// <summary>
114+
/// Contains generic extension members for sequences.
115+
/// </summary>
63116
public static class GenericExtensions
64117
{
118+
/// <summary>
119+
/// Defines extensions for generic sequences.
120+
/// </summary>
121+
/// <typeparam name="TReceiver">The type of elements in the receiver sequence.</typeparam>
122+
/// <param name="source">The sequence used as a receiver.</param>
65123
extension<TReceiver>(IEnumerable<TReceiver> source)
66124
{
125+
/// <summary>
126+
/// Returns a sequence containing a specified number of elements from the source, starting at a given index.
127+
/// </summary>
128+
/// <param name="start">The zero-based index at which to begin retrieving elements. Must be greater than or equal to 0.</param>
129+
/// <param name="count">The number of elements to return. Must be greater than or equal to 0.</param>
130+
/// <returns>
131+
/// An <see cref="IEnumerable{TReceiver}"/> that contains up to <paramref name="count"/> elements from the
132+
/// source sequence, starting at the element at position <paramref name="start"/>. If <paramref name="start"/>
133+
/// is greater than the number of elements in the source, an empty sequence is returned.
134+
/// </returns>
67135
public IEnumerable<TReceiver> Spread(int start, int count)
68136
=> source.Skip(start).Take(count);
69137

138+
/// <summary>
139+
/// Returns a sequence that contains the elements of the original sequence followed by the elements of a
140+
/// specified sequence, each transformed by a converter function.
141+
/// </summary>
142+
/// <remarks>
143+
/// Enumeration of the returned sequence will not start until the sequence is iterated.
144+
/// The converter function is applied to each element of the appended sequence as it is enumerated.
145+
/// </remarks>
146+
/// <typeparam name="TArg">The type of the elements in the sequence to append.</typeparam>
147+
/// <param name="second">The sequence whose elements are to be appended after being converted. Cannot be null.</param>
148+
/// <param name="Converter">A function to convert each element of the appended sequence to the result type. Cannot be null.</param>
149+
/// <returns>
150+
/// An IEnumerable<TReceiver> that contains the elements of the original sequence followed by the converted
151+
/// elements of the specified sequence.
152+
/// </returns>
70153
public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
71154
{
72155
foreach(TReceiver item in source)
@@ -79,11 +162,27 @@ public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg,
79162
}
80163
}
81164

82-
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
165+
/// <summary>
166+
/// Returns a sequence that consists of the elements of the specified collection, transformed by the provided
167+
/// converter, followed by the elements of the current sequence.
168+
/// </summary>
169+
/// <remarks>
170+
/// Enumeration of the returned sequence will not start until the sequence is iterated.
171+
/// Both the input collection and the converter function must not be null; otherwise, an exception will be
172+
/// thrown at enumeration time.
173+
/// </remarks>
174+
/// <typeparam name="TArg">The type of the elements in the collection to prepend.</typeparam>
175+
/// <param name="second">The collection whose elements are to be transformed and prepended to the current sequence. Cannot be null.</param>
176+
/// <param name="converter">A function to convert each element of the prepended collection to the target type. Cannot be null.</param>
177+
/// <returns>
178+
/// An IEnumerable<TReceiver> that contains the converted elements of the specified collection followed by the
179+
/// elements of the current sequence.
180+
/// </returns>
181+
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> converter)
83182
{
84183
foreach (TArg item in second)
85184
{
86-
yield return Converter(item);
185+
yield return converter(item);
87186
}
88187
foreach (TReceiver item in source)
89188
{
Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
---
22
title: "Example XML documentation comments"
33
description: See documentation examples on many different C# language elements. Learn which tags to use in different situations and for different language elements.
4-
ms.date: 07/14/2021
4+
ms.date: 12/11/2025
55
ms.topic: how-to
66
---
77
# Example XML documentation comments
88

9-
This article contains three examples for adding XML documentation comments to most C# language elements. The first example shows how you document a class with different members. The second shows how you would reuse explanations for a hierarchy of classes or interfaces. The third shows tags to use for generic classes and members. The second and third examples use concepts that are covered in the first example.
9+
This article contains three examples for adding XML documentation comments to most C# language elements. The first example shows how you document a class with different members. The second example shows how you can reuse explanations for a hierarchy of classes or interfaces. The third example shows tags to use for generic classes and members. The second and third examples use concepts that are covered in the first example.
1010

1111
## Document a class, struct, or interface
1212

1313
The following example shows common language elements, and the tags you'll likely use to describe these elements. The documentation comments describe the use of the tags, rather than the class itself.
1414

1515
:::code language="csharp" source="./snippets/xmldoc/DocComments.cs" ID="ClassExample":::
1616

17-
Adding documentation can clutter your source code with large sets of comments intended for users of your library. You use the `<Include>` tag to separate your XML comments from your source. Your source code references an XML file with the `<Include>` tag:
17+
Adding documentation can clutter your source code with large sets of comments intended for users of your library. Use the `<Include>` tag to separate your XML comments from your source. Your source code references an XML file by using the `<Include>` tag:
1818

1919
:::code language="csharp" source="./snippets/xmldoc/DocComments.cs" ID="IncludeTag":::
2020

2121
The second file, *xml_include_tag.xml*, contains the documentation comments.
2222

23-
:::code language="xml" source="./snippets/xmldoc/xml_include_tag.xml" :::
23+
:::code language="xml" source="./snippets/xmldoc/xml_include_tag.xml":::
2424

2525
## Document a hierarchy of classes and interfaces
2626

27-
The `<inheritdoc>` element means a type or member *inherits* documentation comments from a base class or interface. You can also use the `<inheritdoc>` element with the `cref` attribute to inherit comments from a member of the same type. The following example shows ways to use this tag. Note that when you add the `inheritdoc` attribute to a type, member comments are inherited. You can prevent the use of inherited comments by writing comments on the members in the derived type. Those will be chosen over the inherited comments.
27+
The `<inheritdoc>` element means a type or member *inherits* documentation comments from a base class or interface. You can also use the `<inheritdoc>` element with the `cref` attribute to inherit comments from a member of the same type. The following example shows ways to use this tag. When you add the `inheritdoc` attribute to a type, member comments are inherited. You can prevent the use of inherited comments by writing comments on the members in the derived type. Those comments are chosen over the inherited comments.
2828

2929
:::code language="csharp" source="./snippets/xmldoc/DocComments.cs" ID="InheritDocTag":::
3030

@@ -34,13 +34,21 @@ Use the `<typeparam>` tag to describe type parameters on generic types and metho
3434

3535
:::code language="csharp" source="./snippets/xmldoc/DocComments.cs" ID="GenericExample":::
3636

37+
## Extension members
38+
39+
Add XML comments for `<summary>`, `<param>`, and, if necessary, `<typeparam>` to describe the receiver parameter on extension members:
40+
41+
:::code language="csharp" source="./snippets/xmldoc/DocComments.cs" ID="ExtensionExample":::
42+
43+
The C# compiler copies the XML nodes from the `extension` block to all members declared in that block.
44+
3745
## Math class example
3846

3947
The following code shows a realistic example of adding doc comments to a math library.
4048

4149
:::code language="csharp" source="./snippets/xmldoc/tagged-library.cs":::
4250

43-
You may find that the code is obscured by all the comments. The final example shows how you would adapt this library to use the `include` tag. You move all the documentation to an XML file:
51+
You might find that the code is obscured by all the comments. The final example shows how you adapt this library to use the `include` tag. You move all the documentation to an XML file:
4452

4553
:::code language="xml" source="./snippets/xmldoc/include.xml":::
4654

@@ -52,4 +60,4 @@ The code uses the `<include>` tag to reference the appropriate element in the XM
5260
- The `file` attribute represents the name of the XML file containing the documentation.
5361
- The `path` attribute represents an [XPath](../../../standard/data/xml/xpath-queries-and-namespaces.md) query to the `tag name` present in the specified `file`.
5462
- The `name` attribute represents the name specifier in the tag that precedes the comments.
55-
- The `id` attribute, which can be used in place of `name`, represents the ID for the tag that precedes the comments.
63+
- The `id` attribute, which you can use in place of `name`, represents the ID for the tag that precedes the comments.

0 commit comments

Comments
 (0)