Reputation: 3990
I have two sets of Tasks, each with different result type:
IEnumerable<Task<T1>> set1 = GetTasksOfT1();
IEnumerable<Task<T2>> set2 = GetTasksOfT2();
Now I would like to await both sets in one line, but I had to project set1 with cast:
await Task.WhenAll(set1.Select(p => p as Task).Concat(set2));
That's because I get this error if I don't use casting:
IEnumerable<Task<T1>>' does not contain a definition for 'Concat'
and the best extension method overload 'Queryable.Concat<Task<T2>>(IQueryable<Task<T2>>, IEnumerable<Task<T2>>)'
requires a receiver of type 'IQueryable<Task<T2>>'
Which is obvious.
So is there a way to use a single WhenAll() without casting?
Edit: The return types T1 and T2 are important - I consume them after the tasks are awaited:
//After await Task.WhenAll(...):
var t1Results = await set1; // all set1 Tasks are completed by now
var t2Results = await set2; // same
DoSomethingWithResults(t1Results, t2Results);
Upvotes: 8
Views: 5557
Reputation: 14846
Although being debatable if creating an array is better than concatenating two enumerables, you could leave the concatenation out of the equation:
await Task.WhenAll(set1, set2);
But, since what you need the is the result of evaluation of each sequence of tasks, I wouldn't bother to await them all as a single set:
DoSomethingWithResults(await set1, await set2);
Upvotes: 0
Reputation: 244837
If you need the results of both sets of Task
s, then it might be simpler to not wait for the all the lists.
I imagine your code (using usr's suggestion) would look like this:
IEnumerable<Task<T1>> set1 = GetTasksOfT1();
IEnumerable<Task<T2>> set2 = GetTasksOfT2();
await Task.WhenAll(set1.Concat<Task>(set2));
var t1Results = await Task.WhenAll(set1);
var t2Results = await Task.WhenAll(set2);
Bu here, the combined await
doesn't really serve any purpose. If you remove it, both result variables will still be initialized correctly, because they have their own await
s.
Upvotes: 1
Reputation: 171178
Actually, the compiler just complains about type inference failure. You can help it with that:
IEnumerable<Task<int>> set1 = null;
IEnumerable<Task<string>> set2 = null;
set1.Concat((IEnumerable<Task>)set2); //#1
((IEnumerable<Task>)set1).Concat(set2); //#2
set1.Concat<Task>(set2); //#3
There are many other ways to provide the type information. This works because IEnumerable
is covariant. This will call Enumerable.Concat<Task>(IEnumerable<Task>, IEnumerable<Task>)
.
Upvotes: 4
Reputation: 53958
If both methods, GetTasksOfT1
and GetTasksOfT2
wouldn't return Task<T>
but Task
, you woulnd't have any problem.
So I suggest either refactor these method to return a sequence of Task objects, IEnumerable<Task>
or pickup the Kote's solution.
Upvotes: 2