tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Extravaganza using ConfigureAwait, await and await foreach

26 Apr 2019 2 mins .NET Core, C#

As I was writing previous post I got some idea to “nest” awaits in the using definition and make it little bit confusing.

Sadly, it didn’t get much confusing, because C# is generally well designed language. This was my try.

class Test : IAsyncDisposable
{
	public ValueTask DisposeAsync() => throw new NotImplementedException();

	public static Task<Test> CreateAsync() => throw new NotImplementedException();

	public async Task Foo()
	{
		await using (var x = await CreateAsync())
		{ }
	}
}

Interesting combination? Sure. Yet still clear what it does. Trying to put ConfigureAwait forces me to extract one await call outside, as I showed in previous post, thus not much luck here.

public async Task Foo()
{
	var t = await CreateAsync().ConfigureAwait(false);
	await using (var x = t.ConfigureAwait(false))
	{ }
}

But I’m not giving up. I can maybe abuse await foreach, another new C# 8 feature, similar to await using. With that I can abuse the IAsyncAnumerable<T> and wrap it into the Task<T> (and obviously that’s weird thing to do).

class Test
{
	public IAsyncEnumerable<Test> Get() => throw new NotImplementedException();
	public Task<IAsyncEnumerable<Test>> Get2() => throw new NotImplementedException();

	public async Task Foo()
	{
		await foreach (var item in Get().ConfigureAwait(false))
		{

		}
	}

	public async Task Foo2()
	{
		await foreach (var item in (await Get2().ConfigureAwait(false)).ConfigureAwait(false))
		{

		}
	}
}

The Foo method is regular ConfigureAwait usage like I described in previous post. But the Foo2 is more odd – which is what I wanted -, because thanks to ConfiguredCancelableAsyncEnumerable<T> I can write it in “one line”.

I should surely rather go back to work. Although as I’m finishing this post, I’ll leave with something I rather not expand further… 😃

class Test : IAsyncDisposable
{
	public async Task Foo()
	{
		await using (var x = ConfigureAwait(false).ConfigureAwait(false).ConfigureAwait(false).ConfigureAwait(false).ConfigureAwait(false).ConfigureAwait(false))
		{ }
	}

	public Test ConfigureAwait(bool continueOnCapturedContext) => this;

	public ValueTask DisposeAsync() => throw new NotImplementedException();
}

Profile Picture Jiří Činčura is .NET, C# and Firebird expert. He focuses on data and business layers, language constructs, parallelism, databases and performance. For almost two decades he contributes to open-source, i.e. FirebirdClient. He works as a senior software engineer for Microsoft. Frequent speaker and blogger at www.tabsoverspaces.com.