diff --git a/docfx.json b/docfx.json index 74f5dbb644c31..b072d87ddc5ac 100644 --- a/docfx.json +++ b/docfx.json @@ -491,7 +491,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": "05/15/2024", + "_csharplang/proposals/*.md": "06/28/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", @@ -662,6 +662,7 @@ "_csharplang/proposals/lock-object.md": "Obey lock object semantics for lock statements", "_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-terators-async.md": "Allow ref and unsafe in iterators and async methods", "_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", @@ -778,6 +779,7 @@ "_csharplang/proposals/lock-object.md": "Special-case how `System.Threading.Lock` interacts with the `lock` keyword by calling its `EnterScope` method. Add static analysis warnings to prevent accidental misuse of the type where possible.", "_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-terators-async.md": "This proposal modifies restrictions to enable ref local variables and unsafe blocks in iterators and async methods", "_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", diff --git a/docs/csharp/language-reference/builtin-types/ref-struct.md b/docs/csharp/language-reference/builtin-types/ref-struct.md index 1d971cb33ca2c..cb547291204f1 100644 --- a/docs/csharp/language-reference/builtin-types/ref-struct.md +++ b/docs/csharp/language-reference/builtin-types/ref-struct.md @@ -1,7 +1,7 @@ --- title: "ref struct types" description: Learn about the ref struct type in C# -ms.date: 10/12/2022 +ms.date: 06/28/2024 --- # `ref` structure types (C# reference) @@ -12,9 +12,9 @@ You can use the `ref` modifier in the declaration of a [structure type](struct.m - A `ref struct` can't implement interfaces. - A `ref struct` can't be boxed to or . - A `ref struct` can't be a type argument. -- A `ref struct` variable can't be captured by a [lambda expression](../operators/lambda-expressions.md) or a [local function](../../programming-guide/classes-and-structs/local-functions.md). -- A `ref struct` variable can't be used in an [`async`](../keywords/async.md) method. However, you can use `ref struct` variables in synchronous methods, for example, in methods that return or . -- A `ref struct` variable can't be used in [iterators](../../iterators.md). +- 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 or . +- 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`. @@ -34,13 +34,13 @@ Beginning with C# 11, you can declare a `ref` field in a `ref struct`, as the fo :::code language="csharp" source="snippets/shared/StructType.cs" id="SnippetRefField"::: -A `ref` field may have the `null` value. Use the method to determine if a `ref` field is `null`. +A `ref` field can have the `null` value. Use the method to determine if a `ref` field is `null`. You can apply the `readonly` modifier to a `ref` field in the following ways: - `readonly ref`: You can [ref reassign](../operators/assignment-operator.md#ref-assignment) such a field with the `= ref` operator only inside a constructor or an [`init` accessor](../keywords/init.md). You can assign a value with the `=` operator at any point allowed by the field access modifier. -- `ref readonly`: At any point, you cannot assign a value with the `=` operator to such a field. However, you can ref reassign a field with the `= ref` operator. -- `readonly ref readonly`: You can only ref reassign such a field in a constructor or an `init` accessor. At any point, you cannot assign a value to the field. +- `ref readonly`: At any point, you can't assign a value with the `=` operator to such a field. However, you can ref reassign a field with the `= ref` operator. +- `readonly ref readonly`: You can only ref reassign such a field in a constructor or an `init` accessor. At any point, you can't assign a value to the field. The compiler ensures that a reference stored in a `ref` field doesn't outlive its referent. diff --git a/docs/csharp/language-reference/compiler-messages/cs1996.md b/docs/csharp/language-reference/compiler-messages/cs1996.md index 4a443c9f95990..8d9b75aae1616 100644 --- a/docs/csharp/language-reference/compiler-messages/cs1996.md +++ b/docs/csharp/language-reference/compiler-messages/cs1996.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS1996" title: "Compiler Error CS1996" -ms.date: 9/12/2022 +ms.date: 7/01/2024 f1_keywords: - "CS1996" helpviewer_keywords: @@ -13,7 +13,7 @@ Cannot await in the body of a lock statement ## Example - The following sample generates CS1996: +The following sample generates CS1996: ```csharp public class C @@ -33,9 +33,11 @@ public class C } ``` +The preceding code produces the same error with C# 13, as the `await` is in the `lock` statement block. + ## To correct this error -Asynchronous code within a `lock` statement block is hard to implement reliably and even harder to implement in a general sense. The C# compiler doesn't support doing this to avoid emitting code that will be prone to deadlocks. Extracting the asynchronous code from the `lock` statement block will correct this error. For example: +Asynchronous code within a `lock` statement block is hard to implement reliably and even harder to implement in a general sense. The C# compiler doesn't support doing this to avoid emitting code prone to deadlocks. Extracting the asynchronous code from the `lock` statement block corrects this error. For example: ```csharp public class C diff --git a/docs/csharp/language-reference/compiler-messages/cs4004.md b/docs/csharp/language-reference/compiler-messages/cs4004.md index 24e701aab2bc9..01fabcd46276b 100644 --- a/docs/csharp/language-reference/compiler-messages/cs4004.md +++ b/docs/csharp/language-reference/compiler-messages/cs4004.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS4004" title: "Compiler Error CS4004" -ms.date: 9/12/2022 +ms.date: 07/01/2024 f1_keywords: - "CS4004" helpviewer_keywords: @@ -13,7 +13,7 @@ Cannot await in an unsafe context ## Example - The following sample generates CS4004: +The following sample generates CS4004: ```csharp using System.Threading.Tasks; @@ -47,11 +47,13 @@ public static class C } ``` +This code generates an error in C# 13 because the `await` is in the `unsafe` block. + The `ReverseText` method naively uses a background task to asynchronously create a new string in reverse order of a given string. ## To correct this error -Separating the unsafe code from the awaitable code will correct this error. One separation technique is creating a new method for the unsafe code and then calling it from the awaitable code. For example: +Separating the unsafe code from the awaitable code corrects this error. One separation technique is creating a new method for the unsafe code and then calling it from the awaitable code. For example: ```csharp public static class C diff --git a/docs/csharp/language-reference/compiler-messages/cs4013.md b/docs/csharp/language-reference/compiler-messages/cs4013.md index 7e03b7f6544c7..71d12c75fe017 100644 --- a/docs/csharp/language-reference/compiler-messages/cs4013.md +++ b/docs/csharp/language-reference/compiler-messages/cs4013.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS4013" title: "Compiler Error CS4013" -ms.date: 9/12/2022 +ms.date: 06/28/2024 f1_keywords: - "CS4013" helpviewer_keywords: @@ -11,6 +11,8 @@ helpviewer_keywords: Instance of type cannot be used inside a nested function, query expression, iterator block or async method +Beginning with C# 13, `ref struct` types can be used in iterator methods, if they aren't accessed across `yield return` statement. + ## Example The following sample generates CS4013: @@ -34,11 +36,11 @@ public class C } ``` -This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan` to improve performance. +This enumerator method extracts lines of text from a character array. It naively tries to use `ReadOnlySpan` to improve performance. The preceding example exhibits the same error in C# 13, because the `ReadOnlySpan` instance `chars` is in scope at the `yield return` statement. ## To correct this error -`Lines(char[] text)` is an enumerator function. An enumerator function compiles the method's body into a state machine that manages the sequence of states the iterator function goes through while processing. That state machine is implemented as a generated class, and the state is implemented as variables within that class. That captured local state is forced from a stack context to a heap context. Since `ref struct`s like `ReadOnlySpan` cannot be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan`, to correct this error, the method must be re-implemented as a non-iterator function, for example: +`Lines(char[] text)` is an enumerator function. An enumerator function compiles the method's body into a state machine that manages the sequence of states the iterator function goes through while processing. That state machine is implemented as a generated class, and the state is implemented as variables within that class. That captured local state is forced from a stack context to a heap context. Since `ref struct`s like `ReadOnlySpan` can't be stored in the heap, the CS4013 error is raised. To continue to use a `ReadOnlySpan`, to correct this error, the method must be reimplemented as a noniterator function, for example: ```csharp public static IEnumerable Lines2(char[] text) @@ -59,7 +61,7 @@ This enumerator method extracts lines of text from a character array. It naivel } ``` -To continue to use an iterator function, to correct this error, the method must be re-implemented to avoid using `ReadOnlySpan`, for example: +To continue to use an iterator function, to correct this error, the method must be reimplemented to avoid using `ReadOnlySpan`, for example: ```csharp public static IEnumerable Lines2(char[] chars) @@ -75,3 +77,27 @@ To continue to use an iterator function, to correct this error, the method must yield return new string(chars, startIndex, chars.Length - startIndex); } ``` + +In C# 13, a `ReadOnlySpan` can be used, but can only be used in code segments without a `yield return`: + +```csharp +static IEnumerable Lines2(char[] text) +{ + ReadOnlySpan chars = text; + + var lines = new List(); + var index = chars.IndexOf('\n'); + while (index > 0) + { + lines.Add(chars[..index].ToString()); + chars = chars[(index + 1)..]; + index = chars.IndexOf('\n'); + } + + lines.Add(chars.ToString()); + foreach(var line in lines) + { + yield return line; + } +} +``` diff --git a/docs/csharp/language-reference/compiler-messages/cs8176.md b/docs/csharp/language-reference/compiler-messages/cs8176.md index 99bef1983de01..21a94a76614c2 100644 --- a/docs/csharp/language-reference/compiler-messages/cs8176.md +++ b/docs/csharp/language-reference/compiler-messages/cs8176.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS8176" title: "Compiler Error CS8176" -ms.date: 9/19/2022 +ms.date: 06/28/2024 f1_keywords: - "CS8176" helpviewer_keywords: @@ -11,7 +11,9 @@ helpviewer_keywords: Iterators cannot have by-reference locals -Iterator blocks use deferred execution, where the evaluation of an expression is delayed until its realized value is actually required. To manage that deferred execution state, iterator blocks use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) cannot be captured within the instance of a class in the heap, so the compiler issues an error. +Iterator blocks use deferred execution, where the evaluation of an expression is delayed until its realized value is required. To manage that deferred execution state, iterator blocks use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) can't be captured within the instance of a class in the heap, so the compiler issues an error. + +Beginning with C# 13, this restriction was removed. ## Example diff --git a/docs/csharp/language-reference/compiler-messages/cs8177.md b/docs/csharp/language-reference/compiler-messages/cs8177.md index d104826eff419..bef37a7d23b59 100644 --- a/docs/csharp/language-reference/compiler-messages/cs8177.md +++ b/docs/csharp/language-reference/compiler-messages/cs8177.md @@ -1,7 +1,7 @@ --- description: "Compiler Error CS8177" title: "Compiler Error CS8177" -ms.date: 9/19/2022 +ms.date: 7/01/2024 f1_keywords: - "CS8177" helpviewer_keywords: @@ -11,11 +11,11 @@ helpviewer_keywords: Async methods cannot have by-reference locals -To manage asynchronous state, `async` methods use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) cannot be captured within the instance of a class in the heap, so the compiler issues an error. +To manage asynchronous state, `async` methods use a state machine, capturing variable state in closures implemented in compiler-generated classes and properties. A local variable reference (on the stack) can't be captured within the instance of a class in the heap, so the compiler issues an error. ## Example - The following sample generates CS8177: +The following sample generates CS8177 before C# 13: ```csharp // CS8177.cs (20,26) @@ -49,7 +49,7 @@ class C ## To correct this error -Changing the variable declaration to remove the `ref` modifier corrects this error: +Remove the `ref` modifier. Or, you can upgrade to C# 13, which ships with .NET 9. ```csharp class C diff --git a/docs/csharp/language-reference/compiler-messages/warning-waves.md b/docs/csharp/language-reference/compiler-messages/warning-waves.md index 0464405050013..642844d733803 100644 --- a/docs/csharp/language-reference/compiler-messages/warning-waves.md +++ b/docs/csharp/language-reference/compiler-messages/warning-waves.md @@ -1,7 +1,7 @@ --- title: "Compiler warning waves" -description: "C# warning waves are optional warnings that can be reported on code where previously a warning wouldn't have been reported. They represent practices that could be harmful, or potentially elements that may be breaking changes in the future." -ms.date: 05/11/2022 +description: "C# warning waves are optional warnings that can be reported on code where previously a warning wouldn't have been reported. They represent practices that could be harmful, or potentially elements that might be breaking changes in the future." +ms.date: 07/01/2024 f1_keywords: - "CS7023" - "CS8073" @@ -39,7 +39,7 @@ helpviewer_keywords: --- # C# Warning waves -New warnings and errors may be introduced in each release of the C# compiler. When new warnings could be reported on existing code, those warnings are introduced under an opt-in system referred to as a *warning wave*. The opt-in system means that you shouldn't see new warnings on existing code without taking action to enable them. Warning waves are enabled using the [**AnalysisLevel**](../compiler-options/errors-warnings.md#analysis-level) element in your project file. When `true` is specified, enabled warning wave warnings generate errors. Warning wave 5 diagnostics were added in C# 9. Warning wave 6 diagnostics were added in C# 10. Warning wave 7 diagnostics were added in C# 11. Warning wave 8 diagnostics were added in C# 12. +New warnings and errors can be introduced in each release of the C# compiler. When new warnings could be reported on existing code, those warnings are introduced under an opt-in system referred to as a *warning wave*. The opt-in system means that you shouldn't see new warnings on existing code without taking action to enable them. Warning waves are enabled using the [**AnalysisLevel**](../compiler-options/errors-warnings.md#analysis-level) element in your project file. When `true` is specified, enabled warning wave warnings generate errors. Warning wave 5 diagnostics were added in C# 9. Warning wave 6 diagnostics were added in C# 10. Warning wave 7 diagnostics were added in C# 11. Warning wave 8 diagnostics were added in C# 12. ## CS9123 - Taking address of local or parameter in async method can create a GC hole. @@ -50,6 +50,8 @@ The following code produces CS9123: :::code language="csharp" source="./snippets/WarningWaves/WaveEight.cs" id="NoAmpersand"::: +Beginning with C# 13, this code generates a compiler error. + ## CS8981 - The type name only contains lower-cased ascii characters. *Warning wave 7* @@ -64,7 +66,7 @@ You can address this warning by renaming the type to include at least one non-lo *Warning wave 6* -This warning corrects some inconsistencies in reporting differences between partial method signatures. The compiler always reported an error when the partial method signatures created different CLR signatures. Now, the compiler reports CS8826 when the signatures are syntactically different C#. Consider the following partial class: +This warning corrects some inconsistencies in reporting differences between partial method signatures. The compiler always reported an error when the partial method signatures created different CLR signatures. Now, the compiler reports CS8826 when the signatures are syntactically different C#. Consider the following partial class: :::code language="csharp" source="./snippets/WarningWaves/WaveSix.cs" id="PartialMethodDeclaration"::: @@ -85,7 +87,7 @@ The `is` and `as` expressions always return `false` for a static type because yo :::code language="csharp" source="./snippets/WarningWaves/WaveFive.cs" id="StaticTypeAsIs"::: -The compiler reports this warning because the type test can never succeed. To correct this warning, remove the test and remove any code executed only if the test succeeded. In the preceding example, the `else` clause is always executed. The method body could be replaced by that single line: +The compiler reports this warning because the type test can never succeed. To correct this warning, remove the test and remove any code executed only if the test succeeded. In the preceding example, the `else` clause is always executed. You can replace that method body with that single line: ```csharp Console.WriteLine("o is not an instance of a static class"); @@ -95,7 +97,7 @@ Console.WriteLine("o is not an instance of a static class"); *Warning wave 5* -The `==` and `!=` operators always return `false` (or `true`) when comparing an instance of a `struct` type to `null`. The following code demonstrates this warning. Assume `S` is a `struct` that has defined `operator ==` and `operator !=`: +The `==` and `!=` operators always return `false` (or `true`) when comparing an instance of a `struct` type to `null`. The following code demonstrates this warning. Assume `S` is a `struct` that defines `operator ==` and `operator !=`: :::code language="csharp" source="./snippets/WarningWaves/WaveFive.cs" id="StructsArentNull"::: @@ -113,7 +115,7 @@ To fix this error, put parentheses around the query expression: :::code language="csharp" source="./snippets/WarningWaves/WaveFive.cs" id="QueryPrecedenceNoWarn"::: -## Members must be fully assigned, use of unassigned variable (CS8880, CS8881, CS8882, CS8883, CS8884, CS8885, CS8886, CS8887) +## Members must be fully assigned. Use of unassigned variable (CS8880, CS8881, CS8882, CS8883, CS8884, CS8885, CS8886, CS8887) *Warning wave 5* diff --git a/docs/csharp/language-reference/keywords/method-parameters.md b/docs/csharp/language-reference/keywords/method-parameters.md index bfbcb6467cf59..d69693fd739d5 100644 --- a/docs/csharp/language-reference/keywords/method-parameters.md +++ b/docs/csharp/language-reference/keywords/method-parameters.md @@ -79,7 +79,7 @@ When you use these modifiers, they describe how the argument is used: - `ref readonly` means the method reads, but can't write the value of the argument. The argument *should* be passed by reference. - `in` means the method reads, but can't write the value of the argument. The argument will be passed by reference or through a temporary variable. -Properties aren't variables. They're methods, and can't be passed to `ref` parameters. You can't use the previous parameter modifiers in the following kinds of methods: +You can't use the previous parameter modifiers in the following kinds of methods: - Async methods, which you define by using the [async](async.md) modifier. - Iterator methods, which include a [yield return](../statements/yield.md) or `yield break` statement. @@ -91,6 +91,8 @@ Properties aren't variables. They're methods, and can't be passed to `ref` param - The `ref readonly` and `in` keywords can't be used unless the first argument is a `struct`. - The `ref readonly` and `in` keywords can't be used on any generic type, even when constrained to be a struct. +Properties aren't variables. They're methods. Properties can't be arguments for `ref` parameters. + ### `ref` parameter modifier To use a `ref` parameter, both the method definition and the calling method must explicitly use the `ref` keyword, as shown in the following example. (Except that the calling method can omit `ref` when making a COM call.) diff --git a/docs/csharp/language-reference/operators/await.md b/docs/csharp/language-reference/operators/await.md index 5e176cddb1ca1..ac4d2609c01d2 100644 --- a/docs/csharp/language-reference/operators/await.md +++ b/docs/csharp/language-reference/operators/await.md @@ -1,7 +1,7 @@ --- title: "await operator - asynchronously wait for a task to complete" description: "The C# `await` operator asynchronously suspends evaluation of the enclosing `async` method." -ms.date: 11/29/2022 +ms.date: 07/01/2024 f1_keywords: - "await_CSharpKeyword" helpviewer_keywords: @@ -23,7 +23,7 @@ The preceding example uses the [async `Main` method](../../fundamentals/program- > [!NOTE] > For an introduction to asynchronous programming, see [Asynchronous programming with async and await](../../asynchronous-programming/index.md). Asynchronous programming with `async` and `await` follows the [task-based asynchronous pattern](../../../standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap.md). -You can use the `await` operator only in a method, [lambda expression](lambda-expressions.md), or [anonymous method](delegate-operator.md) that is modified by the [async](../keywords/async.md) keyword. Within an async method, you can't use the `await` operator in the body of a synchronous function, inside the block of a [lock statement](../statements/lock.md), and in an [unsafe](../keywords/unsafe.md) context. +You can use the `await` operator only in a method, [lambda expression](lambda-expressions.md), or [anonymous method](delegate-operator.md) that is modified by the [async](../keywords/async.md) keyword. Within an async method, you can't use the `await` operator in the body of a synchronous local function, inside the block of a [lock statement](../statements/lock.md), and in an [unsafe](../keywords/unsafe.md) context. The operand of the `await` operator is usually of one of the following .NET types: , , , or . However, any awaitable expression can be the operand of the `await` operator. For more information, see the [Awaitable expressions](~/_csharpstandard/standard/expressions.md#12982-awaitable-expressions) section of the [C# language specification](~/_csharpstandard/standard/README.md). diff --git a/docs/csharp/language-reference/statements/yield.md b/docs/csharp/language-reference/statements/yield.md index 27be16f3c34b1..b36912faa1356 100644 --- a/docs/csharp/language-reference/statements/yield.md +++ b/docs/csharp/language-reference/statements/yield.md @@ -1,7 +1,7 @@ --- title: "yield statement - provide the next element in an iterator" description: "Use the yield statement in iterators to provide the next value or signal the end of an iteration" -ms.date: 11/22/2022 +ms.date: 06/28/2024 f1_keywords: - "yield" - "yield_CSharpKeyword" @@ -22,11 +22,11 @@ You use the `yield` statement in an [iterator](../../iterators.md) to provide th Iteration also finishes when control reaches the end of an iterator. -In the preceding examples, the return type of iterators is (in non-generic cases, use as the return type of an iterator). You can also use as the return type of an iterator. That makes an iterator async. Use the [`await foreach` statement](iteration-statements.md#await-foreach) to iterate over iterator's result, as the following example shows: +In the preceding examples, the return type of iterators is (in nongeneric cases, use as the return type of an iterator). You can also use as the return type of an iterator. That makes an iterator async. Use the [`await foreach` statement](iteration-statements.md#await-foreach) to iterate over iterator's result, as the following example shows: :::code language="csharp" source="snippets/yield/Program.cs" id="IteratorAsync"::: - or can also be the return type of an iterator. That is useful when you implement the `GetEnumerator` method in the following scenarios: + or can also be the return type of an iterator. Use those return types when you implement the `GetEnumerator` method in the following scenarios: - You design the type that implements or interface. - You add an instance or [extension](../../programming-guide/classes-and-structs/extension-methods.md) `GetEnumerator` method to enable iteration over the type's instance with the [`foreach` statement](iteration-statements.md#the-foreach-statement), as the following example shows: @@ -37,7 +37,7 @@ You can't use the `yield` statements in: - methods with [in](../keywords/method-parameters.md#in-parameter-modifier), [ref](../keywords/ref.md), or [out](../keywords/method-parameters.md#out-parameter-modifier) parameters - [lambda expressions](../operators/lambda-expressions.md) and [anonymous methods](../operators/delegate-operator.md) -- methods that contain [unsafe blocks](../keywords/unsafe.md) +- [unsafe blocks](../keywords/unsafe.md). Before C# 13, `yield` was invalid in any method with an `unsafe` block. Beginning with C# 13, you can use `yield` in methods with `unsafe` blocks, but not in the `unsafe` block. ## Execution of an iterator diff --git a/docs/csharp/misc/cs1629.md b/docs/csharp/misc/cs1629.md index 697b90d3239af..5f404a55550c8 100644 --- a/docs/csharp/misc/cs1629.md +++ b/docs/csharp/misc/cs1629.md @@ -1,34 +1,34 @@ --- description: "Compiler Error CS1629" title: "Compiler Error CS1629" -ms.date: 07/20/2015 +ms.date: 06/28/2024 f1_keywords: - "CS1629" helpviewer_keywords: - "CS1629" -ms.assetid: 907eae46-0265-4cd0-b27b-ff555d004259 --- # Compiler Error CS1629 -Unsafe code may not appear in iterators - - The C# language specification does not allow unsafe code in iterators. - - The following sample generates CS1629: - -```csharp -// CS1629.cs +Unsafe code may not appear in iterators + +The C# language specification doesn't allow unsafe code in iterators. This restriction is relaxed in C# 13. You can use `unsafe` blocks, but the `yield return` statement can't be used in an `unsafe` block. + +The following sample generates CS1629: + +```csharp +// CS1629.cs // compile with: /unsafe -using System.Collections.Generic; +using System.Collections.Generic; class C -{ - IEnumerator IteratorMeth() { - int i; - unsafe // CS1629 - { - int *p = &i; - yield return *p; - } - } -} +{ + IEnumerator IteratorMethod() + { + int i; + unsafe // CS1629 + { + int *p = &i; + yield return *p; + } + } +} ``` diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index 3a00dfda0db8d..226cac9d0431e 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -183,6 +183,8 @@ items: href: ../../../_csharplang/proposals/csharp-8.0/nested-stackalloc.md - name: Lock object semantics href: ../../../_csharplang/proposals/lock-object.md + - name: Allow `ref` and `unsafe` + href: ../../../_csharplang/proposals/ref-unsafe-in-iterators-async.md - name: Namespaces items: - name: File scoped namespaces diff --git a/docs/csharp/whats-new/csharp-13.md b/docs/csharp/whats-new/csharp-13.md index b980d17ec6107..c076bc3ebeccb 100644 --- a/docs/csharp/whats-new/csharp-13.md +++ b/docs/csharp/whats-new/csharp-13.md @@ -13,6 +13,7 @@ C# 13 includes the following new features. You can try these features using the - [New escape sequence - `\e`](#new-escape-sequence). - [Method group natural type improvements](#method-group-natural-type) - [Implicit indexer access in object initializers](#implicit-index-access) +- [Enable `ref` locals and `unsafe` contexts in iterators and async methods](#ref-and-unsafe-in-iterators-and-async-methods) C# 13 is supported on **.NET 9**. For more information, see [C# language versioning](../language-reference/configure-language-version.md). @@ -73,6 +74,16 @@ var countdown = new TimerRemaining() The preceding example creates an array that counts down from 9 to 0. In versions before C# 13, the `^` operator can't be used in an object initializer. You need to index the elements from the front. +## `ref` and `unsafe` in iterators and `async` methods + +Before C# 13, iterator methods (methods that use `yield return`) and `async` methods couldn't declare local `ref` variables, nor could they have an `unsafe` context. + +In C# 13, `async` methods can declare `ref` local variables, or local variables of a `ref struct` type. However, those variables can't be accessed across an `await` boundary. Neither can they be accessed across a `yield return` boundary. + +This relaxed restriction enables the compiler to allow verifiably safe use of `ref` local variables and `ref struct` types in more places. You can safely use types like in these methods. The compiler tells you if you violate safety rules. + +In the same fashion, C# 13 allows `unsafe` contexts in iterator methods, provided no unsafe code appears in the same context as the `yield return` statements. + ## See also - [What's new in .NET 9](../../core/whats-new/dotnet-9/overview.md)