Skip to content

Commit d19ced2

Browse files
BillWagneradegeo
andauthored
Add reference and conceptual content for ref struct allowed in interfaces (#41777)
* publish speclet Update config to publish the new speclet for ref struct interfaces. * Update language reference syntax. clarify source and binary break. Turn on preview * Add note for default interface methods * edit pass, address open issues Address open issues on all affected articles, perform a general edit pass. * Update the what's new page with pointers Update the what's new page with pointers to the new content. * fix build issues * Apply suggestions from code review Co-authored-by: Andy (Steve) De George <[email protected]> --------- Co-authored-by: Andy (Steve) De George <[email protected]>
1 parent c626a3c commit d19ced2

File tree

14 files changed

+203
-37
lines changed

14 files changed

+203
-37
lines changed

docfx.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@
5454
"csharp-12.0/*.md",
5555
"lock-object.md",
5656
"method-group-natural-type-improvements.md",
57-
"params-collections.md"
57+
"params-collections.md",
58+
"ref-struct-interfaces.md"
5859
],
5960
"src": "_csharplang/proposals",
6061
"dest": "csharp/language-reference/proposals",
@@ -491,7 +492,7 @@
491492
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
492493
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
493494
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
494-
"_csharplang/proposals/*.md": "06/28/2024",
495+
"_csharplang/proposals/*.md": "07/16/2024",
495496
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
496497
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
497498
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
@@ -663,6 +664,7 @@
663664
"_csharplang/proposals/method-group-natural-type-improvements.md": "Method group natural type improvements",
664665
"_csharplang/proposals/params-collections.md": "Params collections",
665666
"_csharplang/proposals/ref-unsafe-in-iterators-async.md": "Allow ref and unsafe in iterators and async methods",
667+
"_csharplang/proposals/ref-struct-interfaces.md": "Allow ref struct types to implement some interfaces",
666668
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10",
667669
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11",
668670
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12",
@@ -780,6 +782,7 @@
780782
"_csharplang/proposals/method-group-natural-type-improvements.md": "This proposal refines the determination of the natural type of a method group by considering candidates scope-by-scope and pruning at each scope.",
781783
"_csharplang/proposals/params-collections.md": "Allow the `params` modifier on collection types beyond arrays, including `IEnumerable` types.",
782784
"_csharplang/proposals/ref-unsafe-in-iterators-async.md": "This proposal modifies restrictions to enable ref local variables and unsafe blocks in iterators and async methods",
785+
"_csharplang/proposals/ref-struct-interfaces.md": "This proposal provides features that enable interface authors to allow `ref struct` types to implement a particular interface",
783786
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10",
784787
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11",
785788
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12",

docs/csharp/language-reference/attributes/general.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,23 @@
11
---
22
title: "Attributes interpreted by the compiler: Miscellaneous"
3-
ms.date: 10/26/2023
3+
ms.date: 07/26/2024
44
description: "Learn about attributes that affect code generated by the compiler: the Conditional, Obsolete, AttributeUsage, ModuleInitializer, and SkipLocalsInit attributes."
55
---
66
# Miscellaneous attributes interpreted by the C# compiler
77

8-
The attributes `Conditional`, `Obsolete`, `AttributeUsage`, `AsyncMethodBuilder`, `InterpolatedStringHandler`, `ModuleInitializer`, and `Experimental` can be applied to elements in your code. They add semantic meaning to those elements. The compiler uses those semantic meanings to alter its output and report possible mistakes by developers using your code.
8+
There are several attributes that can be applied to elements in your code that add semantic meaning to those elements:
9+
10+
- [`Conditional`](#conditional-attribute): Make execution of a method dependent on a preprocessor identifier.
11+
- [`Obsolete`](#obsolete-attribute): Mark a type or member for (potential) future removal.
12+
- [`AttributeUsage`](#attributeusage-attribute): Declare the language elements where an attribute can be applied.
13+
- [`AsyncMethodBuilder`](#asyncmethodbuilder-attribute): Declare an async method builder type.
14+
- [`InterpolatedStringHandler`](#interpolatedstringhandler-and-interpolatedstringhandlerarguments-attributes): Define an interpolated string builder for a known scenario.
15+
- [`ModuleInitializer`](#moduleinitializer-attribute): Declare a method that initializes a module.
16+
- [`SkipLocalsInit`](#skiplocalsinit-attribute): Elide the code that initializes local variable storage to 0.
17+
- [`UnscopedRef`](#unscopedref-attribute): Declare that a `ref` variable normally interpreted as `scoped` should be treated as unscoped.
18+
- [`Experimental`](#experimental-attribute): Mark a type or member as experimental.
19+
20+
The compiler uses those semantic meanings to alter its output and report possible mistakes by developers using your code.
921

1022
## `Conditional` attribute
1123

@@ -116,7 +128,7 @@ You add the <xref:System.Runtime.CompilerServices.AsyncMethodBuilderAttribute?di
116128
* Has an accessible `GetAwaiter` method.
117129
* The object returned by the `GetAwaiter` method implements the <xref:System.Runtime.CompilerServices.ICriticalNotifyCompletion?displayProperty=nameWithType> interface.
118130

119-
The constructor to the `AsyncMethodBuilder` attribute specifies the type of the associated builder. The builder must implement the following accessible members:
131+
The constructor to the `AsyncMethodBuilder` attribute specifies the type of the associated builder. The builder must implement the following accessible members:
120132

121133
* A static `Create()` method that returns the type of the builder.
122134
* A readable `Task` property that returns the async return type.
@@ -205,6 +217,18 @@ To try this code yourself, set the `AllowUnsafeBlocks` compiler option in your *
205217
</PropertyGroup>
206218
```
207219

220+
## `UnscopedRef` attribute
221+
222+
The `UnscopedRef` attribute marks a variable declaration as unscoped, meaning the reference is allowed to escape.
223+
224+
You add this attribute where the compiler treats a `ref` as implicitly `scoped`:
225+
226+
- The `this` parameter for `struct` instance methods.
227+
- `ref` parameters that refer to `ref struct` types.
228+
- `out` parameters.
229+
230+
Applying the <xref:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute?displayProperty=nameWithType> marks the element as unscoped.
231+
208232
## See also
209233

210234
- <xref:System.Attribute>

docs/csharp/language-reference/attributes/global.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: "Attributes interpreted by the compiler: Global attributes"
3-
ms.date: 12/16/2020
3+
ms.date: 07/26/2024
44
description: Attributes provide metadata the compiler uses to understand more semantics of your program
55
---
66
# Assembly level attributes interpreted by the C# compiler
@@ -29,11 +29,11 @@ The following table shows the identity attributes.
2929
|---------------|-------------|
3030
|<xref:System.Reflection.AssemblyVersionAttribute>|Specifies the version of an assembly.|
3131
|<xref:System.Reflection.AssemblyCultureAttribute>|Specifies which culture the assembly supports.|
32-
|<xref:System.Reflection.AssemblyFlagsAttribute>|Specifies whether an assembly supports side-by-side execution on the same computer, in the same process, or in the same application domain.|
32+
|<xref:System.Reflection.AssemblyFlagsAttribute>|Specifies a bitwise combination of flags for an assembly, describing just-in-time (JIT) compiler options, whether the assembly is retargetable, and whether it has a full or tokenized public key. |
3333

3434
## Informational attributes
3535

36-
You use informational attributes to provide additional company or product information for an assembly. The following table shows the informational attributes defined in the <xref:System.Reflection?displayProperty=nameWithType> namespace.
36+
You use informational attributes to provide more company or product information for an assembly. The following table shows the informational attributes defined in the <xref:System.Reflection?displayProperty=nameWithType> namespace.
3737

3838
|Attribute|Purpose|
3939
|---------------|-------------|

docs/csharp/language-reference/builtin-types/ref-struct.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
---
22
title: "ref struct types"
33
description: Learn about the ref struct type in C#
4-
ms.date: 06/28/2024
4+
ms.date: 07/26/2024
55
---
66
# `ref` structure types (C# reference)
77

88
You can use the `ref` modifier in the declaration of a [structure type](struct.md). Instances of a `ref struct` type are allocated on the stack and can't escape to the managed heap. To ensure that, the compiler limits the usage of `ref struct` types as follows:
99

1010
- A `ref struct` can't be the element type of an array.
1111
- A `ref struct` can't be a declared type of a field of a class or a non-`ref struct`.
12-
- A `ref struct` can't implement interfaces.
1312
- A `ref struct` can't be boxed to <xref:System.ValueType?displayProperty=nameWithType> or <xref:System.Object?displayProperty=nameWithType>.
14-
- A `ref struct` can't be a type argument.
1513
- A `ref struct` variable can't be captured in a [lambda expression](../operators/lambda-expressions.md) or a [local function](../../programming-guide/classes-and-structs/local-functions.md).
1614
- Before C# 13,`ref struct` variables can't be used in an `async` method. Beginning with C# 13, a `ref struct` variable can't be used in the same block as the [`await`](../operators/await.md) expression in an [`async`](../keywords/async.md) method. However, you can use `ref struct` variables in synchronous methods, for example, in methods that return <xref:System.Threading.Tasks.Task> or <xref:System.Threading.Tasks.Task%601>.
1715
- Before C# 13, a `ref struct` variable can't be used in [iterators](../../iterators.md). Beginning with C# 13, `ref struct` types and `ref` locals can be used in iterators, provided they aren't in code segments with the `yield return` statement.
18-
19-
You can define a disposable `ref struct`. To do that, ensure that a `ref struct` fits the [disposable pattern](~/_csharplang/proposals/csharp-8.0/using.md#pattern-based-using). That is, it has an instance `Dispose` method, which is accessible, parameterless and has a `void` return type. You can use the [using statement or declaration](../statements/using.md) with an instance of a disposable `ref struct`.
16+
- Before C# 13, a `ref struct` can't implement interfaces. Beginning with C# 13, a `ref` struct can implement interfaces, but must adhere to the [ref safety](~/_csharpstandard/standard/structs.md#1623-ref-modifier) rules. For example, a `ref struct` type can't be converted to the interface type because that requires a boxing conversion.
17+
- Before C# 13, a `ref struct` can't be a type argument. Beginning with C# 13, a `ref struct` can be the type argument when the type parameter specifies the `allows ref struct` in its `where` clause.
2018

2119
Typically, you define a `ref struct` type when you need a type that also includes data members of `ref struct` types:
2220

@@ -58,6 +56,28 @@ public readonly ref struct Span<T>
5856

5957
The `Span<T>` type stores a reference through which it accesses the contiguous elements in memory. The use of a reference enables a `Span<T>` instance to avoid copying the storage it refers to.
6058

59+
## The disposable pattern
60+
61+
You can define a disposable `ref struct`. To do that, ensure that a `ref struct` fits the [disposable pattern](~/_csharplang/proposals/csharp-8.0/using.md#pattern-based-using). That is, it has an instance `Dispose` method, which is accessible, parameterless and has a `void` return type. You can use the [using statement or declaration](../statements/using.md) with an instance of a disposable `ref struct`.
62+
63+
Beginning with C# 13, you can also implement the <xref:System.IDisposable?displayName=nameWithType> on `ref struct` types. However, overload resolution prefers the disposable pattern to the interface method. The compiler resolves to an `IDisposable.Dispose` method only whan a suitable `Dispose` method isn't found.
64+
65+
## Restrictions for `ref struct` types that implement an interface
66+
67+
These restrictions ensure that a `ref struct` type that implements an interface obeys the necessary [ref safety](~/_csharpstandard/standard/structs.md#1623-ref-modifier) rules.
68+
69+
- A `ref struct` can't be converted to an instance of an interface it implements. This restriction includes the implicit conversion when you use a `ref struct` type as an argument when the parameter is an interface type. The conversion results in a boxing conversion, which violates ref safety.
70+
- A `ref struct` that implements an interface *must* implement all interface members. The `ref struct` must implement members where the interface includes a default implementation.
71+
72+
The compiler enforces these restrictions. If you write `ref struct` types that implement interfaces, each new update might include new [default interface members](../keywords/interface.md#default-interface-members). Until you provide an implementation for these new methods, your application won't compile.
73+
74+
> [!IMPORTANT]
75+
> A `ref struct` that implements an interface includes the potential for later source-breaking and binary-breaking changes. The break occurs if a `ref struct` implements an interface defined in another assembly, and that assembly provides an update which adds default members to that interface.
76+
>
77+
> The source-break happens when you recompile the `ref struct`: It must implement the new member, even though there is a default implementation.
78+
>
79+
> The binary-break happens if you upgrade the external assembly without recompiling the `ref struct` type *and* the updated code calls the default implementation of the new method. The runtime throws an exception when the default member is accessed.
80+
6181
## C# language specification
6282

6383
For more information, see the following sections of the [C# language specification](~/_csharpstandard/standard/README.md):

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
112112
:::row:::
113113
:::column:::
114114
[`add`](add.md)
115-
[`and`](../operators/patterns.md#logical-patterns)
115+
[`allows`](where-generic-type-constraint.md)
116116
[`alias`](extern-alias.md)
117+
[`and`](../operators/patterns.md#logical-patterns)
117118
[`ascending`](ascending.md)
118119
[`args`](../../fundamentals/program-structure/top-level-statements.md#args)
119120
[`async`](async.md)
@@ -122,10 +123,10 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
122123
[`descending`](descending.md)
123124
[`dynamic`](../builtin-types/reference-types.md)
124125
[`equals`](equals.md)
125-
[`file`](file.md)
126-
[`from`](from-clause.md)
127126
:::column-end:::
128127
:::column:::
128+
[`file`](file.md)
129+
[`from`](from-clause.md)
129130
[`get`](get.md)
130131
[`global`](../operators/namespace-alias-qualifier.md)
131132
[`group`](group-clause.md)
@@ -136,9 +137,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
136137
[`managed` (function pointer calling convention)](../unsafe-code.md#function-pointers)
137138
[`nameof`](../operators/nameof.md)
138139
[`nint`](../builtin-types/integral-numeric-types.md)
139-
[`not`](../operators/patterns.md#logical-patterns)
140140
:::column-end:::
141141
:::column:::
142+
[`not`](../operators/patterns.md#logical-patterns)
142143
[`notnull`](../../programming-guide/generics/constraints-on-type-parameters.md#notnull-constraint)
143144
[`nuint`](../builtin-types/integral-numeric-types.md)
144145
[`on`](on.md)

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
description: "Use the `interface` keyword to define contracts that any implementing type must support. Interfaces provide the means to create common behavior among a set of unrelated types."
33
title: "interface keyword"
4-
ms.date: 07/08/2022
4+
ms.date: 07/26/2024
55
f1_keywords:
66
- "interface_CSharpKeyword"
77
helpviewer_keywords:
@@ -15,6 +15,10 @@ In the following example, class `ImplementationClass` must implement a method na
1515

1616
For more information and examples, see [Interfaces](../../fundamentals/types/interfaces.md).
1717

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.
19+
20+
Interface members without an implementation can't include an access modifier. Members with a default implementation can include any access modifier.
21+
1822
## Example interface
1923

2024
[!code-csharp[csrefKeywordsTypes#14](~/samples/snippets/csharp/VS_Snippets_VBCSharp/csrefKeywordsTypes/CS/keywordsTypes.cs#14)]
@@ -28,7 +32,12 @@ An interface can be a member of a namespace or a class. An interface declaration
2832

2933
## Default interface members
3034

31-
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. An interface may include:
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.
36+
37+
> [!IMPORTANT]
38+
> Adding default interfaces members forces any `ref struct` that implements the interface to add an explicit declaration of that member.
39+
40+
An interface may include:
3241

3342
- [Constants](const.md)
3443
- [Operators](../operators/operator-overloading.md)
@@ -58,7 +67,11 @@ public interface INamed
5867
}
5968
```
6069

61-
An interface can inherit from one or more base interfaces. When an interface [overrides a method](override.md) implemented in a base interface, it must use the [explicit interface implementation](../../programming-guide/interfaces/explicit-interface-implementation.md) syntax.
70+
An interface can inherit from one or more base interfaces. When an interface inherits from another interface, a type implementing the derived interface must implement all the members in the base interfaces as well as those declared in the derived interface, as shown in the following code:
71+
72+
:::code language="csharp" source="./snippets/DefineTypes.cs" id="SnippetDerivedInterfaces":::
73+
74+
When an interface [overrides a method](override.md) implemented in a base interface, it must use the [explicit interface implementation](../../programming-guide/interfaces/explicit-interface-implementation.md) syntax.
6275

6376
When a base type list contains a base class and interfaces, the base class must come first in the list.
6477

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,23 @@ static void Main()
4747
}
4848
// Output: My Point: x=2, y=3
4949
//</snippet15>
50+
51+
// <SnippetDerivedInterfaces>
52+
public interface I1
53+
{
54+
void M1();
55+
}
56+
57+
public interface I2 : I1
58+
{
59+
void M2();
60+
}
61+
62+
public class C : I2
63+
{
64+
// implements I1.M1
65+
public void M1() { }
66+
// implements I2.M2
67+
public void M2() { }
68+
}
69+
// </SnippetDerivedInterfaces>

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,17 @@ class NotNullContainer<T>
4949
}
5050
// </Snippet5>
5151

52+
// <SnippetRefStruct>
53+
public class GenericRefStruct<T> where T : allows ref struct
54+
{
55+
// Scoped is allowed because T might be a ref struct
56+
public void M(scoped T parm)
57+
{
58+
59+
}
60+
}
61+
// </SnippetRefStruct>
62+
5263
// <Snippet6>
5364
public interface IMyInterface { }
5465

docs/csharp/language-reference/keywords/snippets/keywords.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
5+
<TargetFramework>net9.0</TargetFramework>
66
<Nullable>enable</Nullable>
77
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
88
<StartupObject>Keywords.Program</StartupObject>
9+
<LangVersion>preview</LangVersion>
910
</PropertyGroup>
1011

1112
<ItemGroup>

0 commit comments

Comments
 (0)