C# 13: Using ref
and unsafe
in Iterators and Async Explained
In earlier versions of C#, using ref
locals in combination with asynchronous methods (async
/await
) was not allowed. Specifically, you could not declare a ref
local variable and use it across await
calls. This limitation existed because await
pauses the execution of the method and resumes it later, which could invalidate the memory location referenced by ref
.
However, C# 13 introduces an enhancement that allows ref
locals to be used across await
calls, enabling more flexible and expressive asynchronous programming, especially when dealing with performance-critical scenarios where passing references is necessary.
Before C# 13
Before C# 13, attempting to use ref
variables across await
calls would result in a compilation error. Here's an example of how this limitation affected asynchronous code.
async Task MyMethodAsync()
{
await FirstAsyncMethod();
// Not allowed before C# 13 — Compiler error
ref int value = ref GetRefValue();
ModifyValue(ref value);
await SecondAsyncMethod();
}
int GetRefValue()
{
// Simulating a reference return
return ref someStorage[0]; // Assume someStorage is an array or similar structure
}
void ModifyValue(ref int x)
{
x *= 2; // Modify the value by reference
}
The compiler would produce an error like this:
The error occurs because the memory referenced by the ref
variable might not be valid when the method resumes after an await
.
Starting with C# 13, you can now declare ref
locals that span across await
calls, as shown in the following example:
using System;
using System.Threading.Tasks;
class Program
{
static int[] someStorage = { 10, 20, 30, 40 }; // Simulating some storage
static async Task Main(string[] args)
{
Console.WriteLine("Before modification: " + someStorage[0]);
await MyMethodAsync();
Console.WriteLine("After modification: " + someStorage[0]);
}
static async Task MyMethodAsync()
{
await FirstAsyncMethod();
// Allowed in C# 13 — ref local can span across await
ref int value = ref GetRefValue();
ModifyValue(ref value);
await SecondAsyncMethod();
}
static ref int GetRefValue()
{
// Simulating a reference return
return ref someStorage[0];
}
static void ModifyValue(ref int x)
{
x *= 2;
}
static async Task FirstAsyncMethod()
{
await Task.Delay(1000);
Console.WriteLine("First async method completed");
}
static async Task SecondAsyncMethod()
{
await Task.Delay(1000);
Console.WriteLine("Second async method completed");
}
}
In this example, ref int value = ref GetRefValue();
spans across await
calls without error, modifying the underlying data asynchronously.
For more insights, you can visit the : https://github.com/dotnet/docs/issues/41204