Skip to content
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

Add reference and conceptual content for ref struct allowed in interfaces #41777

Merged
merged 7 commits into from
Jul 29, 2024
Merged
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 @@ -54,7 +54,8 @@
"csharp-12.0/*.md",
"lock-object.md",
"method-group-natural-type-improvements.md",
"params-collections.md"
"params-collections.md",
"ref-struct-interfaces.md"
],
"src": "_csharplang/proposals",
"dest": "csharp/language-reference/proposals",
Expand Down Expand Up @@ -491,7 +492,7 @@
"_csharplang/proposals/csharp-10.0/*.md": "08/07/2021",
"_csharplang/proposals/csharp-11.0/*.md": "09/30/2022",
"_csharplang/proposals/csharp-12.0/*.md": "08/15/2023",
"_csharplang/proposals/*.md": "06/28/2024",
"_csharplang/proposals/*.md": "07/16/2024",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "09/26/2023",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "06/26/2024",
Expand Down Expand Up @@ -663,6 +664,7 @@
"_csharplang/proposals/method-group-natural-type-improvements.md": "Method group natural type improvements",
"_csharplang/proposals/params-collections.md": "Params collections",
"_csharplang/proposals/ref-unsafe-in-iterators-async.md": "Allow ref and unsafe in iterators and async methods",
"_csharplang/proposals/ref-struct-interfaces.md": "Allow ref struct types to implement some interfaces",
"_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 @@ -780,6 +782,7 @@
"_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.",
"_csharplang/proposals/params-collections.md": "Allow the `params` modifier on collection types beyond arrays, including `IEnumerable` types.",
"_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",
"_csharplang/proposals/ref-struct-interfaces.md": "This proposal provides features that enable interface authors to allow `ref struct` types to implement a particular interface",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11",
"_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12",
Expand Down
30 changes: 27 additions & 3 deletions docs/csharp/language-reference/attributes/general.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
---
title: "Attributes interpreted by the compiler: Miscellaneous"
ms.date: 10/26/2023
ms.date: 07/26/2024
description: "Learn about attributes that affect code generated by the compiler: the Conditional, Obsolete, AttributeUsage, ModuleInitializer, and SkipLocalsInit attributes."
---
# Miscellaneous attributes interpreted by the C# compiler

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.
There are several attributes that can be applied to elements in your code that add semantic meaning to those elements:

- [`Conditional`](#conditional-attribute): Make execution of a method dependent on a preprocessor identifier.
- [`Obsolete`](#obsolete-attribute): Mark a type or member for (potential) future removal.
- [`AttributeUsage`](#attributeusage-attribute): Declare the language elements where an attribute can be applied.
- [`AsyncMethodBuilder`](#asyncmethodbuilder-attribute): Declare an async method builder type.
- [`InterpolatedStringHandler`](#interpolatedstringhandler-and-interpolatedstringhandlerarguments-attributes): Define an interpolated string builder for a known scenario.
- [`ModuleInitializer`](#moduleinitializer-attribute): Declare a method that initializes a module.
- [`SkipLocalsInit`](#skiplocalsinit-attribute): Elide the code that initializes local variable storage to 0.
- [`UnscopedRef`](#unscopedref-attribute): Declare that a `ref` variable normally interpreted as `scoped` should be treated as unscoped.
- [`Experimental`](#experimental-attribute): Mark a type or member as experimental.

The compiler uses those semantic meanings to alter its output and report possible mistakes by developers using your code.

## `Conditional` attribute

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

The constructor to the `AsyncMethodBuilder` attribute specifies the type of the associated builder. The builder must implement the following accessible members:
The constructor to the `AsyncMethodBuilder` attribute specifies the type of the associated builder. The builder must implement the following accessible members:

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

## `UnscopedRef` attribute

The `UnscopedRef` attribute marks a variable declaration as unscoped, meaning the reference is allowed to escape.

You add this attribute where the compiler treats a `ref` as implicitly `scoped`:

- The `this` parameter for `struct` instance methods.
- `ref` parameters that refer to `ref struct` types.
- `out` parameters.

Applying the <xref:System.Diagnostics.CodeAnalysis.UnscopedRefAttribute?displayProperty=nameWithType> marks the element as unscoped.

## See also

- <xref:System.Attribute>
Expand Down
6 changes: 3 additions & 3 deletions docs/csharp/language-reference/attributes/global.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Attributes interpreted by the compiler: Global attributes"
ms.date: 12/16/2020
ms.date: 07/26/2024
description: Attributes provide metadata the compiler uses to understand more semantics of your program
---
# Assembly level attributes interpreted by the C# compiler
Expand Down Expand Up @@ -29,11 +29,11 @@ The following table shows the identity attributes.
|---------------|-------------|
|<xref:System.Reflection.AssemblyVersionAttribute>|Specifies the version of an assembly.|
|<xref:System.Reflection.AssemblyCultureAttribute>|Specifies which culture the assembly supports.|
|<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.|
|<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. |

## Informational attributes

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

|Attribute|Purpose|
|---------------|-------------|
Expand Down
30 changes: 25 additions & 5 deletions docs/csharp/language-reference/builtin-types/ref-struct.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
---
title: "ref struct types"
description: Learn about the ref struct type in C#
ms.date: 06/28/2024
ms.date: 07/26/2024
---
# `ref` structure types (C# reference)

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:

- A `ref struct` can't be the element type of an array.
- A `ref struct` can't be a declared type of a field of a class or a non-`ref struct`.
- A `ref struct` can't implement interfaces.
- A `ref struct` can't be boxed to <xref:System.ValueType?displayProperty=nameWithType> or <xref:System.Object?displayProperty=nameWithType>.
- A `ref struct` can't be a type argument.
- 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).
- 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>.
- 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.

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

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

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

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.

## The disposable pattern

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

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.

## Restrictions for `ref struct` types that implement an interface

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.

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

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.

> [!IMPORTANT]
> 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.
>
> The source-break happens when you recompile the `ref struct`: It must implement the new member, even though there is a default implementation.
>
> 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.

## C# language specification

For more information, see the following sections of the [C# language specification](~/_csharpstandard/standard/README.md):
Expand Down
9 changes: 5 additions & 4 deletions docs/csharp/language-reference/keywords/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
:::row:::
:::column:::
[`add`](add.md)
[`and`](../operators/patterns.md#logical-patterns)
[`allows`](where-generic-type-constraint.md)
[`alias`](extern-alias.md)
[`and`](../operators/patterns.md#logical-patterns)
[`ascending`](ascending.md)
[`args`](../../fundamentals/program-structure/top-level-statements.md#args)
[`async`](async.md)
Expand All @@ -122,10 +123,10 @@ 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)
[`file`](file.md)
[`from`](from-clause.md)
:::column-end:::
:::column:::
[`file`](file.md)
[`from`](from-clause.md)
[`get`](get.md)
[`global`](../operators/namespace-alias-qualifier.md)
[`group`](group-clause.md)
Expand All @@ -136,9 +137,9 @@ A contextual keyword is used to provide a specific meaning in the code, but it i
[`managed` (function pointer calling convention)](../unsafe-code.md#function-pointers)
[`nameof`](../operators/nameof.md)
[`nint`](../builtin-types/integral-numeric-types.md)
[`not`](../operators/patterns.md#logical-patterns)
:::column-end:::
:::column:::
[`not`](../operators/patterns.md#logical-patterns)
[`notnull`](../../programming-guide/generics/constraints-on-type-parameters.md#notnull-constraint)
[`nuint`](../builtin-types/integral-numeric-types.md)
[`on`](on.md)
Expand Down
19 changes: 16 additions & 3 deletions docs/csharp/language-reference/keywords/interface.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
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."
title: "interface keyword"
ms.date: 07/08/2022
ms.date: 07/26/2024
f1_keywords:
- "interface_CSharpKeyword"
helpviewer_keywords:
Expand All @@ -15,6 +15,10 @@ In the following example, class `ImplementationClass` must implement a method na

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

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.

Interface members without an implementation can't include an access modifier. Members with a default implementation can include any access modifier.

## Example interface

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

## Default interface members

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

> [!IMPORTANT]
> Adding default interfaces members forces any `ref struct` that implements the interface to add an explicit declaration of that member.

An interface may include:

- [Constants](const.md)
- [Operators](../operators/operator-overloading.md)
Expand Down Expand Up @@ -58,7 +67,11 @@ public interface INamed
}
```

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

:::code language="csharp" source="./snippets/DefineTypes.cs" id="SnippetDerivedInterfaces":::

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.

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

Expand Down
20 changes: 20 additions & 0 deletions docs/csharp/language-reference/keywords/snippets/DefineTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,23 @@ static void Main()
}
// Output: My Point: x=2, y=3
//</snippet15>

// <SnippetDerivedInterfaces>
public interface I1
{
void M1();
}

public interface I2 : I1
{
void M2();
}

public class C : I2
{
// implements I1.M1
public void M1() { }
// implements I2.M2
public void M2() { }
}
// </SnippetDerivedInterfaces>
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ class NotNullContainer<T>
}
// </Snippet5>

// <SnippetRefStruct>
public class GenericRefStruct<T> where T : allows ref struct
{
// Scoped is allowed because T might be a ref struct
public void M(scoped T parm)
{

}
}
// </SnippetRefStruct>

// <Snippet6>
public interface IMyInterface { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<StartupObject>Keywords.Program</StartupObject>
<LangVersion>preview</LangVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading
Loading