Skip to content

Commit 4dd6d45

Browse files
authored
Don't disallow adding a DIM to an interface (#48801)
1 parent c1f4826 commit 4dd6d45

File tree

3 files changed

+35
-32
lines changed

3 files changed

+35
-32
lines changed

docs/core/compatibility/library-change-rules.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
---
22
title: .NET API changes that affect compatibility
33
description: Learn how .NET attempts to maintain compatibility for developers across .NET versions, and what kind of change is considered a breaking change.
4-
ms.date: 05/12/2021
4+
ms.date: 09/30/2025
55
ms.topic: article
66
---
77
# Change rules for compatibility
88

99
Throughout its history, .NET has attempted to maintain a high level of compatibility from version to version and across implementations of .NET. Although .NET 5 (and .NET Core) and later versions can be considered as a new technology compared to .NET Framework, two major factors limit the ability of this implementation of .NET to diverge from .NET Framework:
1010

1111
- A large number of developers either originally developed or continue to develop .NET Framework applications. They expect consistent behavior across .NET implementations.
12-
13-
- .NET Standard library projects allow developers to create libraries that target common APIs shared by .NET Framework and .NET 5 (and .NET Core) and later versions. Developers expect that a library used in a .NET 5 application should behave identically to the same library used in a .NET Framework application.
12+
- .NET Standard library projects allow developers to create libraries that target common APIs shared by .NET Framework and .NET 5 (and .NET Core) and later versions. Developers expect that a library used in a .NET application should behave identically to the same library used in a .NET Framework application.
1413

1514
Along with compatibility across .NET implementations, developers expect a high level of compatibility across versions of a given implementation of .NET. In particular, code written for an earlier version of .NET Core should run seamlessly on .NET 5 or a later version. In fact, many developers expect that the new APIs found in newly released versions of .NET should also be compatible with the pre-release versions in which those APIs were introduced.
1615

@@ -122,9 +121,11 @@ Changes in this category modify the public surface area of a type. Most of the c
122121

123122
This includes removing or renaming a getter or setter from a property, as well as renaming or removing enumeration members.
124123

125-
-**DISALLOWED: Adding a member to an interface**
124+
-**REQUIRES JUDGMENT: Adding a member to an interface**
125+
126+
While it's a breaking change in the sense that it raises your minimum .NET version to .NET Core 3.0 (C# 8.0), which is when [default interface members](../../csharp/language-reference/keywords/interface.md#default-interface-members) (DIMs) were introduced, adding a static, non-abstract, non-virtual member to an interface is allowed.
126127

127-
If you [provide an implementation](../../csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md), adding a new member to an existing interface won't necessarily result in compile failures in downstream assemblies. However, not all languages support default interface members (DIMs). Also, in some scenarios, the runtime can't decide which default interface member to invoke. For these reasons, adding a member to an existing interface is considered a breaking change.
128+
If you [provide an implementation](../../csharp/advanced-topics/interface-implementation/default-interface-methods-versions.md), adding a new member to an existing interface won't necessarily result in compile failures in downstream assemblies. However, not all languages support DIMs. Also, in some scenarios, the runtime can't decide which default interface member to invoke. In some scenarios, interfaces are implemented by `ref struct` types. Because `ref struct` types can't be boxed, they can't be converted to interface types. Therefore, `ref struct` types must provide an implicit implementation for every interface member. They can't make use of the default implementation provided by the interface. For these reasons, use judgment when adding a member to an existing interface.
128129

129130
-**DISALLOWED: Changing the value of a public constant or enumeration member**
130131

@@ -153,9 +154,8 @@ Changes in this category modify the public surface area of a type. Most of the c
153154
-**DISALLOWED: Adding the [virtual](../../csharp/language-reference/keywords/virtual.md) keyword to a member**
154155

155156
While this often is not a breaking change because the C# compiler tends to emit [callvirt](<xref:System.Reflection.Emit.OpCodes.Callvirt>) Intermediate Language (IL) instructions to call non-virtual methods (`callvirt` performs a null check, while a normal call doesn't), this behavior is not invariable for several reasons:
156-
157-
- C# is not the only language that .NET targets.
158157

158+
- C# is not the only language that .NET targets.
159159
- The C# compiler increasingly tries to optimize `callvirt` to a normal call whenever the target method is non-virtual and is probably not null (such as a method accessed through the [?. null propagation operator](../../csharp/language-reference/operators/member-access-operators.md#null-conditional-operators--and-)).
160160

161161
Making a method virtual means that the consumer code would often end up calling it non-virtually.

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

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,50 @@ helpviewer_keywords:
99
---
1010
# :::no-loc text="interface"::: (C# Reference)
1111

12-
An interface defines a contract. Any [`class`](class.md), [`record`](../builtin-types/record.md) or [`struct`](../builtin-types/struct.md) that implements that contract must provide an implementation of the members defined in the interface. An interface may define a default implementation for members. It may also define [`static`](static.md) members in order to provide a single implementation for common functionality. Beginning with C# 11, an interface may define `static abstract` or `static virtual` members to declare that an implementing type must provide the declared members. Typically, `static virtual` methods declare that an implementation must define a set of [overloaded operators](../operators/operator-overloading.md).
12+
An interface defines a contract. Any [`class`](class.md), [`record`](../builtin-types/record.md) or [`struct`](../builtin-types/struct.md) that implements that contract must provide an implementation of the members defined in the interface.
13+
14+
An interface can define a [default implementation](#default-interface-members) for a member. It can also define [`static`](static.md) members to provide a single implementation for common functionality.
15+
16+
Beginning with C# 11, an interface can define `static abstract` or `static virtual` members to declare that an implementing type must provide the declared members. Typically, `static virtual` methods declare that an implementation must define a set of [overloaded operators](../operators/operator-overloading.md).
1317

1418
In the following example, class `ImplementationClass` must implement a method named `SampleMethod` that has no parameters and returns `void`.
1519

20+
[!code-csharp[csrefKeywordsTypes#14](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsTypes/CS/keywordsTypes.cs#14)]
21+
1622
For more information and examples, see [Interfaces](../../fundamentals/types/interfaces.md).
1723

18-
A top-level interface, one declared in a namespace but not nested inside another type, can be declared `public` or `internal`. The default is `internal`. Nested interface declarations, those declared inside another type, can be declared using any access modifier.
24+
## Access modifiers
1925

20-
Interface members without an implementation can't include an access modifier. Members with a default implementation can include any access modifier.
26+
An interface can be a member of a namespace or a class. A top-level interface, one declared in a namespace but not nested inside another type, can be declared `public` or `internal`. The default is `internal`. Nested interface declarations, those declared inside another type, can be declared using any access modifier.
2127

22-
## Example interface
28+
Interface members *without* an implementation (abstract members) are implicitly `public` and cannot have any other access modifier. Interface members *with* a default implementation are `private` by default if no access modifier is specified, but can be declared with any access modifier (`public`, `private`, `protected`, or `internal`).
2329

24-
[!code-csharp[csrefKeywordsTypes#14](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsTypes/CS/keywordsTypes.cs#14)]
30+
## Interface members
2531

26-
An interface can be a member of a namespace or a class. An interface declaration can contain declarations (signatures without any implementation) of the following members:
32+
An interface declaration can contain the following members:
2733

28-
- [Methods](../../programming-guide/classes-and-structs/methods.md)
29-
- [Properties](../../programming-guide/classes-and-structs/using-properties.md)
30-
- [Indexers](../../programming-guide/indexers/using-indexers.md)
31-
- [Events](event.md)
34+
- [Methods](../../programming-guide/classes-and-structs/methods.md).
35+
- [Properties](../../programming-guide/classes-and-structs/using-properties.md).
36+
- [Indexers](../../programming-guide/indexers/using-indexers.md).
37+
- [Events](event.md).
38+
- [Constants](const.md).
39+
- [Operators](../operators/operator-overloading.md).
40+
- [A static constructor](../../programming-guide/classes-and-structs/constructors.md#static-constructors).
41+
- [Nested types](../../programming-guide/classes-and-structs/nested-types.md).
42+
- [Static fields, methods, properties, indexers, and events](static.md).
43+
- [Member declarations using the explicit interface implementation syntax](~/_csharplang/proposals/csharp-8.0/default-interface-methods.md#explicit-implementation-in-interfaces).
44+
- Explicit access modifiers (the default access for abstract methods is [`public`](access-modifiers.md)).
3245

3346
## Default interface members
3447

35-
These preceding member declarations typically don't contain a body. An interface member may declare a body. Member bodies in an interface are the *default implementation*. Members with bodies permit the interface to provide a "default" implementation for classes and structs that don't provide an overriding implementation.
48+
Member declarations typically don't contain a body, however, an interface member can declare a body. Member bodies in an interface are the *default implementation*. Members with bodies permit the interface to provide a "default" implementation for classes and structs that don't provide an overriding implementation.
3649

3750
> [!IMPORTANT]
3851
> Adding default interfaces members forces any `ref struct` that implements the interface to add an explicit declaration of that member.
3952
40-
An interface may include:
41-
42-
- [Constants](const.md)
43-
- [Operators](../operators/operator-overloading.md)
44-
- [Static constructor](../../programming-guide/classes-and-structs/constructors.md#static-constructors).
45-
- [Nested types](../../programming-guide/classes-and-structs/nested-types.md)
46-
- [Static fields, methods, properties, indexers, and events](static.md)
47-
- [Member declarations using the explicit interface implementation syntax](~/_csharplang/proposals/csharp-8.0/default-interface-methods.md#explicit-implementation-in-interfaces).
48-
- Explicit access modifiers (the default access is [`public`](access-modifiers.md)).
49-
5053
## Static abstract and virtual members
5154

52-
Beginning with C# 11, an interface may declare `static abstract` and `static virtual` members for all member types except fields. Interfaces can declare that implementing types must define operators or other static members. This feature enables generic algorithms to specify number-like behavior. You can see examples in the numeric types in the .NET runtime, such as <xref:System.Numerics.INumber%601?displayProperty=nameWithType>. These interfaces define common mathematical operators that are implemented by many numeric types. The compiler must resolve calls to `static virtual` and `static abstract` methods at compile time. The `static virtual` and `static abstract` methods declared in interfaces don't have a runtime dispatch mechanism analogous to `virtual` or `abstract` methods declared in classes. Instead, the compiler uses type information available at compile time. Therefore, `static virtual` methods are almost exclusively declared in [generic interfaces](../../programming-guide/generics/generic-interfaces.md). Furthermore, most interfaces that declare `static virtual` or `static abstract` methods declare that one of the type parameters must [implement the declared interface](../../programming-guide/generics/constraints-on-type-parameters.md#type-arguments-implement-declared-interface). For example, the `INumber<T>` interface declares that `T` must implement `INumber<T>`. The compiler uses the type argument to resolve calls to the methods and operators declared in the interface declaration. For example, the `int` type implements `INumber<int>`. When the type parameter `T` denotes the type argument `int`, the `static` members declared on `int` are invoked. Alternatively, when `double` is the type argument, the `static` members declared on the `double` type are invoked.
55+
Beginning with C# 11, an interface can declare `static abstract` and `static virtual` members for all member types except fields. Interfaces can declare that implementing types must define operators or other static members. This feature enables generic algorithms to specify number-like behavior. You can see examples in the numeric types in the .NET runtime, such as <xref:System.Numerics.INumber%601?displayProperty=nameWithType>. These interfaces define common mathematical operators that are implemented by many numeric types. The compiler must resolve calls to `static virtual` and `static abstract` methods at compile time. The `static virtual` and `static abstract` methods declared in interfaces don't have a runtime dispatch mechanism analogous to `virtual` or `abstract` methods declared in classes. Instead, the compiler uses type information available at compile time. Therefore, `static virtual` methods are almost exclusively declared in [generic interfaces](../../programming-guide/generics/generic-interfaces.md). Furthermore, most interfaces that declare `static virtual` or `static abstract` methods declare that one of the type parameters must [implement the declared interface](../../programming-guide/generics/constraints-on-type-parameters.md#type-arguments-implement-declared-interface). For example, the `INumber<T>` interface declares that `T` must implement `INumber<T>`. The compiler uses the type argument to resolve calls to the methods and operators declared in the interface declaration. For example, the `int` type implements `INumber<int>`. When the type parameter `T` denotes the type argument `int`, the `static` members declared on `int` are invoked. Alternatively, when `double` is the type argument, the `static` members declared on the `double` type are invoked.
5356

5457
> [!IMPORTANT]
5558
> Method dispatch for `static abstract` and `static virtual` methods declared in interfaces is resolved using the compile time type of an expression. If the runtime type of an expression is derived from a different compile time type, the static methods on the base (compile time) type will be called.
@@ -58,7 +61,7 @@ You can try this feature by working with the tutorial on [static abstract member
5861

5962
## Interface inheritance
6063

61-
Interfaces may not contain instance state. While static fields are now permitted, instance fields aren't permitted in interfaces. [Instance auto-properties](../../programming-guide/classes-and-structs/auto-implemented-properties.md) aren't supported in interfaces, as they would implicitly declare a hidden field. This rule has a subtle effect on property declarations. In an interface declaration, the following code doesn't declare an automatically implemented property as it does in a `class` or `struct`. Instead, it declares a property that doesn't have a default implementation but must be implemented in any type that implements the interface:
64+
Interfaces can't contain instance state. While static fields are now permitted, instance fields aren't permitted in interfaces. [Instance auto-properties](../../programming-guide/classes-and-structs/auto-implemented-properties.md) aren't supported in interfaces, as they would implicitly declare a hidden field. This rule has a subtle effect on property declarations. In an interface declaration, the following code doesn't declare an automatically implemented property as it does in a `class` or `struct`. Instead, it declares a property that doesn't have a default implementation but must be implemented in any type that implements the interface:
6265

6366
```csharp
6467
public interface INamed

docs/csharp/whats-new/csharp-version-history.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ The C# 9 release continues the work to keep C# a modern, general-purpose program
196196
C# 8.0 is the first major C# release that specifically targets .NET Core. Some features rely on new Common Language Runtime (CLR) capabilities, others on library types added only in .NET Core. C# 8.0 adds the following features and enhancements to the C# language:
197197

198198
- [Readonly members](../language-reference/builtin-types/struct.md#readonly-instance-members)
199-
- [Default interface methods](../language-reference/keywords/interface.md#default-interface-members)
199+
- [Default interface members](../language-reference/keywords/interface.md#default-interface-members)
200200
- [Pattern matching enhancements](../language-reference/operators/patterns.md):
201201
- Switch expressions
202202
- Property patterns
@@ -213,7 +213,7 @@ C# 8.0 is the first major C# release that specifically targets .NET Core. Some f
213213
- [Stackalloc in nested expressions](../language-reference/operators/stackalloc.md)
214214
- [Enhancement of interpolated verbatim strings](../language-reference/tokens/interpolated.md)
215215

216-
Default interface members require enhancements in the CLR. Those features were added in the CLR for .NET Core 3.0. Ranges and indexes, and asynchronous streams require new types in the .NET Core 3.0 libraries. Nullable reference types, while implemented in the compiler, is much more useful when libraries are annotated to provide semantic information regarding the null state of arguments and return values. Those annotations are being added in the .NET Core libraries.
216+
Default interface members require enhancements in the CLR. Those features were added in the CLR for .NET Core 3.0. Ranges, indexes, and asynchronous streams require new types in the .NET Core 3.0 libraries. Nullable reference types, while implemented in the compiler, is much more useful when libraries are annotated to provide semantic information regarding the null state of arguments and return values. Those annotations are being added in the .NET Core libraries.
217217

218218
## C# version 7.3
219219

0 commit comments

Comments
 (0)