tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Extravaganza using ConfigureAwait, await and await foreach

26 Apr 2019 .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 an independent developer focusing on data and business layers, language constructs, parallelism and databases. Specifically Entity Framework, asynchronous and parallel programming, cloud and Azure. He's Microsoft Most Valuable Professional and you can read his articles, guides, tips and tricks at www.tabsoverspaces.com.