Ignore for a moment the absurdity of await
ing an Enumerable.Range
call. It's just there to elicit the crash-y behavior. It just as easily could be a method that's doing some network IO to build a collection of value objects. (Indeed, this was where I saw the crash occur.)
If I comment out the Interlocked.Exchange
line, the compiler doesn't crash.
public class Launcher
{
private static IEnumerable<int> _foo;
static void Main(string[] args)
{
DoWorkAsync().Wait();
}
private static async Task DoWorkAsync()
{
RefreshCache();
foreach (var element in _foo)
{
Console.WriteLine(element);
}
Console.ReadLine();
}
private static async void RefreshCache()
{
Interlocked.Exchange(ref _foo, await Cache());
}
private static async Task<IEnumerable<int>> Cache()
{
return Enumerable.Range(0, 10);
}
}
Changing RefreshCache()
to this keeps the compiler from crashing:
private static async void RefreshCache()
{
var foo = await Cache();
Interlocked.Exchange(ref _foo, foo);
}
Edit:
An even simpler, self contained reproduce provided by @Servy
public class Worker
{
private static int foo;
private static async void DoWork()
{
DoNothing(ref foo, await Task.FromResult(0));
}
private static void DoNothing(ref int item, int other)
{
}
}