Skip to content

Commit d3acc5b

Browse files
committed
Update language reference syntax.
clarify source and binary break. Turn on preview
1 parent f5856e6 commit d3acc5b

File tree

7 files changed

+123
-12
lines changed

7 files changed

+123
-12
lines changed

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@ description: "Learn about attributes that affect code generated by the compiler:
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

@@ -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.Runtime.CompilerServices.UnscopedRef?displayProperty=nameWithType> marks the element as unscoped.
231+
208232
## See also
209233

210234
- <xref:System.Attribute>

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

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@ You can use the `ref` modifier in the declaration of a [structure type](struct.m
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. Only if a suitable `Dispose` method isn't found will an `IDisposable.Dispose` method be chosen.
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 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 may 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/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/where-generic-type-constraint.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ f1_keywords:
99
- "classconstraint_CSharpKeyword"
1010
- "structconstraint_CSharpKeyword"
1111
- "enumconstraint_CSharpKeyword"
12+
- "allows_CsharpKeyword"
1213
helpviewer_keywords:
1314
- "where (generic type constraint) [C#]"
1415
---
@@ -58,7 +59,13 @@ The `where` clause may also include a constructor constraint, `new()`. That cons
5859

5960
:::code language="csharp" source="snippets/GenericWhereConstraints.cs" ID="Snippet5":::
6061

61-
The `new()` constraint appears last in the `where` clause. The `new()` constraint can't be combined with the `struct` or `unmanaged` constraints. All types satisfying those constraints must have an accessible parameterless constructor, making the `new()` constraint redundant.
62+
The `new()` constraint appears last in the `where` clause, unless it is followed by the `allows ref struct` anti-constraint. The `new()` constraint can't be combined with the `struct` or `unmanaged` constraints. All types satisfying those constraints must have an accessible parameterless constructor, making the `new()` constraint redundant.
63+
64+
This anti-constraint declares that the type argument for `T` can be a `ref struct` type. For example:
65+
66+
:::code language="csharp" source="snippets/GenericWhereConstraints.cs" ID="SnippetRefStruct":::
67+
68+
The generic type or method must obey ref safety rules for any instance of `T` because it might be a `ref struct`. The `allows ref struct` clause can't be combined with the `class` or `class?` constraint. The `allows ref struct` anti-constraint must follow all constraints for that type argument.
6269

6370
With multiple type parameters, use one `where` clause for each type parameter, for example:
6471

docs/csharp/programming-guide/generics/constraints-on-type-parameters.md

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,18 @@ Constraints inform the compiler about the capabilities a type argument must have
3030
|`where T :` *\<interface name>?*|The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic. In a nullable context, `T` can be a nullable reference type, a non-nullable reference type, or a value type. `T` can't be a nullable value type.|
3131
|`where T : U`|The type argument supplied for `T` must be or derive from the argument supplied for `U`. In a nullable context, if `U` is a non-nullable reference type, `T` must be a non-nullable reference type. If `U` is a nullable reference type, `T` can be either nullable or non-nullable. |
3232
|`where T : default`|This constraint resolves the ambiguity when you need to specify an unconstrained type parameter when you override a method or provide an explicit interface implementation. The `default` constraint implies the base method without either the `class` or `struct` constraint. For more information, see the [`default` constraint](~/_csharplang/proposals/csharp-9.0/unconstrained-type-parameter-annotations.md#default-constraint) spec proposal.|
33+
|`where T : allows ref struct`|This anti-constraint declares that the type argument for `T` can be a `ref struct` type. The generic type or method must obey ref safety rules for any instance of `T` because it might be a `ref struct`.|
3334

3435
Some constraints are mutually exclusive, and some constraints must be in a specified order:
3536

3637
- You can apply at most one of the `struct`, `class`, `class?`, `notnull`, and `unmanaged` constraints. If you supply any of these constraints, it must be the first constraint specified for that type parameter.
3738
- The base class constraint, (`where T : Base` or `where T : Base?`), can't be combined with any of the constraints `struct`, `class`, `class?`, `notnull`, or `unmanaged`.
3839
- You can apply at most one base class constraint, in either form. If you want to support the nullable base type, use `Base?`.
3940
- You can't name both the non-nullable and nullable form of an interface as a constraint.
40-
- The `new()` constraint can't be combined with the `struct` or `unmanaged` constraint. If you specify the `new()` constraint, it must be the last constraint for that type parameter.
41+
- The `new()` constraint can't be combined with the `struct` or `unmanaged` constraint. If you specify the `new()` constraint, it must be the last constraint for that type parameter. Anti-constraints, if applicable, can follow the `new()` constraint.
4142
- The `default` constraint can be applied only on override or explicit interface implementations. It can't be combined with either the `struct` or `class` constraints.
43+
- The `allows ref struct` clause can't be combined with the `class` or `class?` constraint.
44+
- The `allows ref struct` anti-constraint must follow all constraints for that type parameter.
4245

4346
## Why use constraints
4447

@@ -161,6 +164,50 @@ public interface IAdditionSubtraction<T> where T : IAdditionSubtraction<T>
161164

162165
The preceding syntax would require implementers to use [explicit interface implementation](../interfaces/explicit-interface-implementation.md) for those methods. Providing the extra constraint enables the interface to define the operators in terms of the type parameters. Types that implement the interface can implicitly implement the interface methods.
163166

167+
## Allows ref struct
168+
169+
The `allows ref struct` anti-constraint declares that the corresponding type argument can be a [`ref struct`](../../language-reference/builtin-types/ref-struct.md) type. Instances of that type parameter must obey the following rules:
170+
171+
- It can't be boxed.
172+
- It participates in [ref safety rules](~/_csharpstandard/standard/structs.md#16412-safe-context-constraint).
173+
- Instances can't be used where a `ref struct` type isn't allowed, such as `static` fields.
174+
- Instances can be marked with the `scoped` modifier.
175+
176+
The `allows ref struct` clause isn't inherited. In the following code:
177+
178+
```csharp
179+
class C<T, S>
180+
where T : allows ref struct
181+
where S : T
182+
{
183+
// etc
184+
}
185+
```
186+
187+
The argument for `S` can't be a `ref struct` because `S` doesn't have the `allows ref struct` clause.
188+
189+
A type parameter that has the `allows ref struct` clause can't be used as a type argument unless the corresponding type parameter also has the `allows ref struct` clause. This rule is demonstrated in the following example:
190+
191+
```csharp
192+
public class Allow<T> where T : allows ref struct
193+
{
194+
195+
}
196+
197+
public class Disallow<T>
198+
{
199+
}
200+
201+
public class Example<T> where T : allows ref struct
202+
{
203+
private Allow<T> fieldOne; // Allowed. T is allowed to be a ref struct
204+
205+
private Disallow<T> fieldTwo; // Error. T is not allowed to be a ref struct
206+
}
207+
```
208+
209+
The preceding sample shows that a type argument that might be a `ref struct` type can't be substituted for a type parameter that can't be a `ref struct` type.
210+
164211
## See also
165212

166213
- <xref:System.Collections.Generic>

docs/csharp/programming-guide/generics/snippets/generics.csproj

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

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

0 commit comments

Comments
 (0)