diff --git a/docs/csharp/language-reference/keywords/extension.md b/docs/csharp/language-reference/keywords/extension.md index 5b3c2f395e4e3..741cdfeec25c4 100644 --- a/docs/csharp/language-reference/keywords/extension.md +++ b/docs/csharp/language-reference/keywords/extension.md @@ -1,7 +1,7 @@ --- title: "Extension member declarations" 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." -ms.date: 01/21/2026 +ms.date: 07/08/2026 f1_keywords: - "extension_CSharpKeyword" - "extension" @@ -12,7 +12,7 @@ Starting with C# 14, top-level, nongeneric `static class` declarations can use ` [!INCLUDE[csharp-version-note](../includes/initial-version.md)] -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. +The `extension` block specifies the type and receiver for extension members. You can declare methods, properties, indexers, and 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. > [!NOTE] > 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. @@ -66,9 +66,22 @@ The equivalent extension method declarations demonstrate how those type paramete :::code language="csharp" source="./snippets/ExtensionMethods.cs" id="GenericExtensionMethods"::: +## Extension indexers + +Starting with C# 15, you can declare *indexers* in an `extension` block. An extension indexer lets you index into a receiver as though the indexer were declared on the receiver type. Because indexers are always instance members, an extension block that declares an indexer must provide a named receiver parameter. Extension indexers support the same features as ordinary indexers, including `get` and `set` accessors, expression-bodied accessors, and ref-returning accessors. + +The following example declares a get-only indexer on `IEnumerable` that returns the element at a specified position: + +:::code language="csharp" source="./snippets/extensions.cs" id="ExtensionIndexer"::: + +You index into the receiver as though the indexer were a member of the receiver type: + +:::code language="csharp" source="./snippets/extensions.cs" id="UseExtensionIndexer"::: + ## See also - [Extensions feature specification](~/_csharplang/proposals/csharp-14.0/extensions.md) +- [Extension indexers feature specification](~/_csharplang/proposals/extension-indexers.md) ## C# language specification diff --git a/docs/csharp/language-reference/keywords/snippets/Extensions.cs b/docs/csharp/language-reference/keywords/snippets/Extensions.cs index b4f8ae7dadd5e..3b8343a37339d 100644 --- a/docs/csharp/language-reference/keywords/snippets/Extensions.cs +++ b/docs/csharp/language-reference/keywords/snippets/Extensions.cs @@ -202,6 +202,28 @@ public IEnumerable Prepend(IEnumerable second, Func +// +/// +/// Contains an extension indexer for numeric sequences. +/// +public static class SequenceIndexer +{ + /// + /// Defines an indexer for integer sequences. + /// + /// The sequence used as a receiver. + extension(IEnumerable sequence) + { + /// + /// Gets the element at the specified position in the sequence. + /// + /// The zero-based position of the element to retrieve. + /// The element at the specified position. + public int this[int index] => sequence.ElementAt(index); + } +} +// + public static class ExtensionExamples { public static void BasicExample() @@ -219,5 +241,9 @@ public static void BasicExample() var newSequence = IEnumerable.Generate(5, 10, 2); var identity = IEnumerable.Identity; // + + // + int third = numbers[2]; + // } } diff --git a/docs/csharp/language-reference/xmldoc/examples.md b/docs/csharp/language-reference/xmldoc/examples.md index a95f4fb6f0655..933ed211d55b1 100644 --- a/docs/csharp/language-reference/xmldoc/examples.md +++ b/docs/csharp/language-reference/xmldoc/examples.md @@ -44,6 +44,13 @@ Add XML comments for ``, ``, and, if necessary, `` to The C# compiler copies the XML nodes from the `extension` block to all members declared in that block. +For an extension indexer, `cref` syntax can reference the indexer and its accessors through the `extension` block that declares it, and it can reference the implementation methods on the containing class. The following examples show how to reference the `this[int]` indexer and its generated `get_Item` accessor from the preceding example: + +```csharp +/// +/// +``` + ## Math class example The following code shows a realistic example of adding doc comments to a math library. diff --git a/docs/csharp/language-reference/xmldoc/snippets/xmldoc/DocComments.cs b/docs/csharp/language-reference/xmldoc/snippets/xmldoc/DocComments.cs index cf8ec17faffd7..a852a1b31e471 100644 --- a/docs/csharp/language-reference/xmldoc/snippets/xmldoc/DocComments.cs +++ b/docs/csharp/language-reference/xmldoc/snippets/xmldoc/DocComments.cs @@ -362,6 +362,13 @@ public static IEnumerable Generate(int count, Func generat yield return generator(); } } + + /// + /// Gets the element at the specified position in the sequence. + /// + /// The zero-based position of the element to retrieve. + /// The element at the specified position. + public TSequence this[int index] => sequence.ElementAt(index); } } // diff --git a/docs/csharp/language-reference/xmldoc/snippets/xmldoc/xmldoc.csproj b/docs/csharp/language-reference/xmldoc/snippets/xmldoc/xmldoc.csproj index 556d004c40ed7..40c7c345f8c80 100644 --- a/docs/csharp/language-reference/xmldoc/snippets/xmldoc/xmldoc.csproj +++ b/docs/csharp/language-reference/xmldoc/snippets/xmldoc/xmldoc.csproj @@ -3,6 +3,7 @@ Exe net10.0 + preview enable enable xmldoc.xml diff --git a/docs/csharp/whats-new/csharp-15.md b/docs/csharp/whats-new/csharp-15.md index 176a6f0842cf6..7375d7b119f11 100644 --- a/docs/csharp/whats-new/csharp-15.md +++ b/docs/csharp/whats-new/csharp-15.md @@ -1,7 +1,7 @@ --- title: What's new in C# 15 description: Get an overview of the new features in C# 15. C# 15 ships with .NET 11. -ms.date: 06/16/2026 +ms.date: 07/08/2026 ms.topic: whats-new ms.update-cycle: 365-days ai-usage: ai-assisted @@ -13,6 +13,7 @@ C# 15 includes the following new features. Try these features by using the lates - [Collection expression arguments](#collection-expression-arguments) - [Union types](#union-types) - [Closed hierarchies](#closed-hierarchies) +- [Extension indexers](#extension-indexers) - [Memory safety](#memory-safety) C# 15 is the latest C# preview release. .NET 11 preview versions support C# 15. For more information, see [C# language versioning](../language-reference/configure-language-version.md). @@ -108,6 +109,31 @@ The `closed` modifier is a contextual keyword. A `closed` class is implicitly `a For more information, see the [closed modifier](../language-reference/keywords/closed.md) and [Closed hierarchy patterns](../language-reference/operators/patterns.md#closed-hierarchy-patterns) in the language reference, or the [feature specification](~/_csharplang/proposals/closed-hierarchies.md). You can copy the examples in this section, including the `ClosedAttribute` workaround, from the [keywords snippets project](https://github.com/dotnet/docs/blob/main/docs/csharp/language-reference/keywords/snippets/shared) in the `dotnet/docs` GitHub repository. +## Extension indexers + +Starting with C# 15, you can declare *indexers* in an `extension` block. Extension indexers let you index into a receiver as though the indexer were declared on the receiver type. Because indexers are always instance members, an extension block that declares an indexer must provide a named receiver parameter. + +The following example declares a get-only indexer on `IEnumerable` that returns the element at a specified position: + +```csharp +public static class SequenceIndexer +{ + extension(IEnumerable sequence) + { + public int this[int index] => sequence.ElementAt(index); + } +} +``` + +You index into the receiver as though the indexer were a member of the receiver type: + +```csharp +IEnumerable numbers = Enumerable.Range(1, 10); +int third = numbers[2]; +``` + +For more information, see [Extension declaration](../language-reference/keywords/extension.md#extension-indexers) in the language reference or the [feature specification](~/_csharplang/proposals/extension-indexers.md). + ## Memory safety C# 15 begins a multirelease effort to redefine memory safety in the language. The goal is to tie the `unsafe` context to the operations that actually access unmanaged memory, rather than to the existence of pointer types. Most memory safety vulnerabilities come from these access operations, so the language makes them stand out for reviewers and auditors.