Skip to content

Add language reference and fundamentals for C# 14 extension members #45654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
"first-class-span-types.md",
"simple-lambda-parameters-with-modifiers.md",
"partial-events-and-constructors.md",
"null-conditional-assignment.md"
"null-conditional-assignment.md",
"extensions.md"
],
"src": "_csharplang/proposals",
"dest": "csharp/language-reference/proposals",
Expand Down Expand Up @@ -510,7 +511,7 @@
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
"_csharplang/proposals/csharp-13.0/*.md": "10/31/2024",
"_csharplang/proposals/*.md": "04/04/2025",
"_csharplang/proposals/*.md": "04/08/2025",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "11/08/2023",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "11/09/2024",
Expand Down Expand Up @@ -691,6 +692,7 @@
"_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "Simple lambda parameters with modifiers",
"_csharplang/proposals/partial-events-and-constructors.md": "Partial events and constructors",
"_csharplang/proposals/null-conditional-assignment.md": "Null conditional assignment",
"_csharplang/proposals/extensions.md": "Extension members",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
Expand Down Expand Up @@ -817,6 +819,7 @@
"_csharplang/proposals/simple-lambda-parameters-with-modifiers.md": "This proposal allows lambda parameters to be declared with modifiers without requiring their type names. You can add modifiers like `ref` and `out` to lambda parameters without specifying their type.",
"_csharplang/proposals/partial-events-and-constructors.md": "This proposal allows partial events and constructors to be declared in partial classes. The event and constructor can be split across class declarations.",
"_csharplang/proposals/null-conditional-assignment.md": "This proposal allows the null conditional operator to be used for the destination of assignment expressions. This allows you to assign a value to a property or field only if the left side is not null.",
"_csharplang/proposals/extensions.md": "This proposal enables new kinds of extension members. These new extension members support extension properties, extension static members, including extension operators.",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13",
Expand Down
2 changes: 1 addition & 1 deletion docs/core/whats-new/dotnet-10/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ C# 14 introduces several new features and enhancements to improve developer prod
- **Unbound generic support for `nameof`**: The `nameof` expression now supports unbound generic types, such as `List<>`, returning the name of the type without requiring type arguments.
- **Implicit span conversions**: Introduces first-class support for `Span<T>` and `ReadOnlySpan<T>` with new implicit conversions, enabling more natural programming with these types.
- **Modifiers on simple lambda parameters**: Allows parameter modifiers like `ref`, `in`, or `out` in lambda expressions without specifying parameter types.
- **Experimental feature - String literals in data section**: Enables emitting string literals as UTF-8 data into a separate section of the PE file, improving efficiency for certain scenarios.
- **Partial events and constructors**: Adds support for partial instance constructors and partial events, complementing partial methods and properties introduced in C# 13.
- **Extension members**: Extension methods now support static methods, instance properties, and static properties through `extension` blocks, enabling more flexible and powerful extensions.
- **Null-conditional assignment**: Simplifies conditional assignments by allowing properties or fields to be updated only if the containing instance exists, using the `?.` operator.
- **Experimental feature - String literals in data section**: Enables emitting string literals as UTF-8 data into a separate section of the PE file, improving efficiency for certain scenarios.

For more information, see [What's new in C# 14](../../../csharp/whats-new/csharp-14.md).

Expand Down
16 changes: 8 additions & 8 deletions docs/csharp/fundamentals/object-oriented/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Classes, structs, and records"
description: Describes the use of classes, structures (structs), and records in C#.
ms.date: 03/23/2022
ms.date: 04/11/2025
helpviewer_keywords:
- "structs [C#], about structs"
- "records [C#], about records"
Expand All @@ -14,17 +14,17 @@ helpviewer_keywords:
---
# Overview of object oriented techniques in C\#

In C#, the definition of a type&mdash;a class, struct, or record&mdash;is like a blueprint that specifies what the type can do. An object is basically a block of memory that has been allocated and configured according to the blueprint. This article provides an overview of these blueprints and their features. The [next article in this series](objects.md) introduces objects.
In C#, the definition of a type&mdash;a class, struct, or record&mdash;is like a blueprint that specifies what the type can do. An object is basically a block of memory allocated and configured according to the blueprint. This article provides an overview of these blueprints and their features. The [next article in this series](objects.md) introduces objects.

## Encapsulation

*Encapsulation* is sometimes referred to as the first pillar or principle of object-oriented programming. A class or struct can specify how accessible each of its members is to code outside of the class or struct. Methods and variables that aren't intended to be used from outside of the class or assembly can be hidden to limit the potential for coding errors or malicious exploits. For more information, see the [Object-oriented programming](../tutorials/oop.md) tutorial.
*Encapsulation* is sometimes referred to as the first pillar or principle of object-oriented programming. A class or struct can specify how accessible each of its members is to code outside of the class or struct. Member not intended for consumers outside of the class or assembly are hidden to limit the potential for coding errors or malicious exploits. For more information, see the [Object-oriented programming](../tutorials/oop.md) tutorial.

## Members

The *members* of a type include all methods, fields, constants, properties, and events. In C#, there are no global variables or methods as there are in some other languages. Even a program's entry point, the `Main` method, must be declared within a class or struct (implicitly when you use [top-level statements](../program-structure/top-level-statements.md)).

The following list includes all the various kinds of members that may be declared in a class, struct, or record.
The following list includes all the various kinds of members that can be declared in a class, struct, or record.

- Fields
- Constants
Expand Down Expand Up @@ -56,7 +56,7 @@ The default accessibility is `private`.

Classes (but not structs) support the concept of inheritance. A class that derives from another class, called the *base class*, automatically contains all the public, protected, and internal members of the base class except its constructors and finalizers.

Classes may be declared as [abstract](../../language-reference/keywords/abstract.md), which means that one or more of their methods have no implementation. Although abstract classes cannot be instantiated directly, they can serve as base classes for other classes that provide the missing implementation. Classes can also be declared as [sealed](../../language-reference/keywords/sealed.md) to prevent other classes from inheriting from them.
Classes can be declared as [abstract](../../language-reference/keywords/abstract.md), which means that one or more of their methods have no implementation. Although abstract classes can't be instantiated directly, they can serve as base classes for other classes that provide the missing implementation. Classes can also be declared as [sealed](../../language-reference/keywords/sealed.md) to prevent other classes from inheriting from them.

For more information, see [Inheritance](./inheritance.md) and [Polymorphism](./polymorphism.md).

Expand All @@ -66,7 +66,7 @@ Classes, structs, and records can implement multiple interfaces. To implement fr

## Generic Types

Classes, structs, and records can be defined with one or more type parameters. Client code supplies the type when it creates an instance of the type. For example, the <xref:System.Collections.Generic.List%601> class in the <xref:System.Collections.Generic> namespace is defined with one type parameter. Client code creates an instance of a `List<string>` or `List<int>` to specify the type that the list will hold. For more information, see [Generics](../types/generics.md).
Classes, structs, and records can be defined with one or more type parameters. Client code supplies the type when it creates an instance of the type. For example, the <xref:System.Collections.Generic.List%601> class in the <xref:System.Collections.Generic> namespace is defined with one type parameter. Client code creates an instance of a `List<string>` or `List<int>` to specify the type that the list holds. For more information, see [Generics](../types/generics.md).

## Static Types

Expand All @@ -86,9 +86,9 @@ You can instantiate and initialize class or struct objects, and collections of o

## Anonymous Types

In situations where it isn't convenient or necessary to create a named class you use anonymous types. Anonymous types are defined by their named data members. For more information, see [Anonymous types](../types/anonymous-types.md).
In situations where it isn't convenient or necessary to create a named class you use anonymous types. Named data members define anonymous types. For more information, see [Anonymous types](../types/anonymous-types.md).

## Extension Methods
## Extension Members

You can "extend" a class without creating a derived class by creating a separate type. That type contains methods that can be called as if they belonged to the original type. For more information, see [Extension methods](../../programming-guide/classes-and-structs/extension-methods.md).

Expand Down
30 changes: 11 additions & 19 deletions docs/csharp/language-reference/keywords/base.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,34 @@
---
title: "base keyword"
title: "The base keyword"
description: Learn about the base keyword, which is used to access members of the base class from within a derived class in C#.
ms.date: 07/20/2015
ms.date: 04/11/2025
f1_keywords:
- "base"
- "BaseClass.BaseClass"
- "base_CSharpKeyword"
helpviewer_keywords:
- "base keyword [C#]"
ms.assetid: 8b645dbe-1a33-49b8-8716-1c401f9a5ea5
---
# base (C# Reference)
# The base keyword

The `base` keyword is used to access members of the base class from within a derived class. Use it if you want to:

- Call a method on the base class that has been overridden by another method.

- Call a method on the base class overridden by another method.
- Specify which base-class constructor should be called when creating instances of the derived class.

The base class access is permitted only in a constructor, in an instance method, and in an instance property accessor.

Using the `base` keyword from within a static method will give an error.
The base class access is permitted only in a constructor, in an instance method, and in an instance property accessor. Using the `base` keyword from within a static method produces an error.

The base class that is accessed is the base class specified in the class declaration. For example, if you specify `class ClassB : ClassA`, the members of ClassA are accessed from ClassB, regardless of the base class of ClassA.

## Example 1

In this example, both the base class `Person` and the derived class `Employee` have a method named `GetInfo`. By using the `base` keyword, it is possible to call the `GetInfo` method of the base class from within the derived class.
In this example, both the base class `Person` and the derived class `Employee` have a method named `GetInfo`. By using the `base` keyword, it's possible to call the `GetInfo` method of the base class from within the derived class.

[!code-csharp[csrefKeywordsAccess#1](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsAccess/CS/csrefKeywordsAccess.cs#1)]

For additional examples, see [new](new-modifier.md), [virtual](virtual.md), and [override](override.md).

## Example 2
:::code language="csharp" source="./snippets/csrefKeywordsAccess.cs" id="snippet1":::

This example shows how to specify the base-class constructor called when creating instances of a derived class.

[!code-csharp[csrefKeywordsAccess#2](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsAccess/CS/csrefKeywordsAccess.cs#2)]
:::code language="csharp" source="./snippets/csrefKeywordsAccess.cs" id="snippet2":::

For more examples, see [new](new-modifier.md), [virtual](virtual.md), and [override](override.md).

## C# language specification

Expand All @@ -45,4 +37,4 @@ This example shows how to specify the base-class constructor called when creatin
## See also

- [C# Keywords](./index.md)
- [this](./this.md)
- [The `this` keyword](./this.md)
55 changes: 55 additions & 0 deletions docs/csharp/language-reference/keywords/extension.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
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: 04/11/2025
f1_keywords:
- "extension_CSharpKeyword"
- "extension"
---
# Extension declaration (C# Reference)

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.

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.

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

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

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

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

You can declare any number of members in a single container, 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:

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

Static extensions can be called as though they are static members of the receiver type:

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

> [!IMPORTANT]
> 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.

The following example shows an extension method using the `this` modifier:

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

The `Add` method can be called from any other method as though it was a member of the `IEnumerable<int>` interface:

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

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.

TODO: Add Generic extension members.
- Type parameter on receiver

Check failure on line 45 in docs/csharp/language-reference/keywords/extension.md

View workflow job for this annotation

GitHub Actions / lint

Lists should be surrounded by blank lines

docs/csharp/language-reference/keywords/extension.md:45 MD032/blanks-around-lists Lists should be surrounded by blank lines [Context: "- Type parameter on receiver"] https://github.com/DavidAnson/markdownlint/blob/v0.37.4/doc/md032.md
- Type parameter that's not the receiver on a member
- Type parameter on receiver + additional

## See also

- [Extensions feature specification](~/_csharplang/proposals/extensions.md)

## C# language specification

[!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]
7 changes: 4 additions & 3 deletions docs/csharp/language-reference/keywords/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
description: "C# Keywords: Find the reference material for the predefined keywords and contextual keywords defined in the C# language."
title: "C# Keywords and contextual keywords"
ms.date: 08/14/2024
ms.date: 04/11/2025
f1_keywords:
- "cs.keywords"
helpviewer_keywords:
Expand Down Expand Up @@ -123,6 +123,7 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
[`descending`](descending.md)
[`dynamic`](../builtin-types/reference-types.md)
[`equals`](equals.md)
[`extension`](extension.md)
:::column-end:::
:::column:::
[`field`](field.md)
Expand All @@ -137,9 +138,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
[`let`](let-clause.md)
[`managed` (function pointer calling convention)](../unsafe-code.md#function-pointers)
[`nameof`](../operators/nameof.md)
[`nint`](../builtin-types/integral-numeric-types.md)
:::column-end:::
:::column:::
[`nint`](../builtin-types/integral-numeric-types.md)
[`not`](../operators/patterns.md#logical-patterns)
[`notnull`](../../programming-guide/generics/constraints-on-type-parameters.md#notnull-constraint)
[`nuint`](../builtin-types/integral-numeric-types.md)
Expand All @@ -151,9 +152,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
[`record`](../../fundamentals/types/records.md)
[`remove`](remove.md)
[`required`](required.md)
[`scoped`](../statements/declarations.md#scoped-ref)
:::column-end:::
:::column:::
[`scoped`](../statements/declarations.md#scoped-ref)
[`select`](select-clause.md)
[`set`](set.md)
[`unmanaged` (function pointer calling convention)](../unsafe-code.md#function-pointers)
Expand Down
Loading
Loading