tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Fusing await using and await foreach and await

22 Mar 2021 2 mins C#

As I’m thinking about the await using and various usage patterns, my mind sometimes starts to wander into pieces like Task<IAsyncDisposable>, Task<IAsyncEnumerable<T>> and Task<IAsyncEnumerable<IAsyncDisposable>>. And how to wrap that into await using and await foreach making some confusing combinations. This is what I was playing with, don’t expect anything specific.

The starting point was this piece of code.

public async Task Foo()
{
	await foreach (var item in await Test<int>())
	{ }
	static Task<IAsyncEnumerable<T>> Test<T>() => default;
}

The only interesting piece is the Task<IAsyncEnumerable<T>>. What would that represent? 🤔 The rest is just a combination of await foreach and await inside it.

That got me into next piece of code.

public async Task Bar()
{
	await using var test = await Test();
	static Task<IAsyncDisposable> Test() => default;
}

Here, I’m trying to somehow fuse together the await using and await without extra stuff in the middle.

Maybe I can skip the variable declaration and have only the await expression inside.

public async Task Baz()
{
	await using await Test();
	static Task<IAsyncDisposable> Test() => default;
}

Sadly, this is invalid code. The correct way to write it would be…

public async Task Baz()
{
	await using (await Test());
	static Task<IAsyncDisposable> Test() => default;
}

But the parentheses bring some order into it and that’s not what I’m about at the moment.

Maybe I can create some crazy type and abuse that.

public class Damn : IAsyncDisposable, IAsyncEnumerable<Damn>
{
	public ValueTask DisposeAsync() => default;
	public IAsyncEnumerator<Damn> GetAsyncEnumerator(CancellationToken cancellationToken = default) => default;
	public TaskAwaiter<Damn> GetAwaiter() => default;
}

This type allows me to write stuff like this.

public async Task M(Damn damn) 
{
	await using (await await damn) 
		await foreach (var item in await await damn)
			await await item;
}

Which is bit weird, but the formatting kind of makes it easy to understand. Single line version looks bit more confusing.

public async Task M(Damn damn) 
{
	await using (await await damn) await foreach (var item in await await damn) await await item;
}

Obviously with the Damn type I can keep “spinning”.

public async Task M(Damn damn) 
{
	await using (await await damn)
		await foreach (var item in await await damn)
			await using (await await item)
				await foreach (var _ in await await item)
					await await _;
}

But it’s the same pattern being repeated, not really something different.

After that I got tired and stopped. Feel free to be inspired and share your crazy ideas.

Profile Picture Jiří Činčura is an independent developer, .NET, C# and Firebird expert, focusing on data and business layers, language constructs, parallelism, databases and performance. He's Microsoft Most Valuable Professional and frequent speaker. You can read his articles, guides and tips and tricks at www.tabsoverspaces.com.