C# 13: Using ref and unsafe in Iterators and Async Explained

sharmila subbiah
.Net Programming
Published in
2 min readSep 24, 2024

--

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

--

--

sharmila subbiah
.Net Programming

With over a decade of experience in the tech industry, I currently hold the position of Senior Software Engineer at Youlend.