Consider the following ideal code (which doesn't work). I'm essentially trying to create a list of Task
s that return a specific object, associate them with a string
identifier, then execute all of them in bulk with Task.WhenAll
. At the end of the execution, I need to have the results of those Task
s still associated with the string identifiers they were originally created with:
public async Task<SomeObject> DoSomethingAsync(string thing)
{
// implementation elided
}
public async Task<SomeObject> DoSomethingElseAsync(string thing)
{
// different implementation elided
}
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = new List<(string, Task<SomeObject>)>();
foreach (var item in someListOfStrings)
{
if (condition)
{
tasks.Add((item, DoSomethingAsync(item)));
}
else
{
tasks.Add((item, DoSomethingElseAsync(item)));
}
}
// this doesn't compile, i'm just demonstrating what i want to achieve
var results = await Task.WhenAll(tasks);
return results;
}
This can be rewritten to the following:
public async Task<(string, SomeObject)> DoSomethingWrapperAsync(string thing)
=> (thing, await DoSomethingAsync(thing));
public async Task<(string, SomeObject)> DoSomethingElseWrapperAsync(string thing)
=> (thing, await DoSomethingElseAsync(thing));
public async Task<IEnumerable<(string, SomeObject)>>
DoManyThingsAsync(IEnumerable<string> someListOfStrings, bool condition)
{
var tasks = new List<Task<(string, SomeObject)>>();
foreach (var thing in someListOfStrings)
{
if (condition)
{
tasks.Add(DoSomethingWrapperAsync(thing));
}
else
{
tasks.Add(DoSomethingElseWrapperAsync(thing));
}
}
// this does compile
var results = await Task.WhenAll(tasks);
return results;
}
The problem is that I need an extra wrapper method for every possible discrete async function I'm going to call, which feels unnecessary and wasteful and is a lot of code (because there will be MANY of these methods). Is there a simpler way of achieving what I need?
I looked into implementing the awaitable/awaiter pattern, but can't see how I could get it to work with Task.WhenAll
which requires a collection of Task
or Task<TResult>
, since the guidance seems to be "don't extend those classes".