Replies: 16 comments 1 reply
-
You can use extension method for this purpose. related to dotnet/roslyn#16159 |
Beta Was this translation helpful? Give feedback.
-
@ufcpp Ahh, I did not realise that the compiler was happy to look for Perhaps my query then is actually that this extension method should be in the standard library? For what it's worth, I've implemented the simplest method I can to implement this: public static class TupleExtensions
{
public static TaskAwaiter<(T1, T2)> GetAwaiter<T1, T2>(this(Task<T1>, Task<T2>) tasks)
=> tasks.Transpose().GetAwaiter();
public static async Task<(T1, T2)> Transpose<T1, T2>(this(Task<T1>, Task<T2>) tasks)
{
var (task1, task2) = tasks;
await Task.WhenAll(task1, task2);
return (task1.Result, task2.Result);
}
} |
Beta Was this translation helpful? Give feedback.
-
My questions regarding this are:
If tuple element names shouldn't be propagated and Otherwise, this will have to be implemented by the compiler. |
Beta Was this translation helpful? Give feedback.
-
public static TaskAwaiter<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> GetAwaiter<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this (Task<T1>, Task<T2>, Task<T3>, Task<T4>, Task<T5>, Task<T6>, Task<T7>, Task<T8>, Task<T9>, Task<T10>) tasks) {
// logic here
} Where compiler support would come in is if we would want |
Beta Was this translation helpful? Give feedback.
-
@HaloFour My (not well articulated) point was that the compiler supports, say, tuple with 100 elements, but you don't want 100+ overloads of So, if you want to implement this using a library, you need to stop at some fairly small number of elements (though you're right it wouldn't have to be 7). And the point about custom awaitables is also a good one. |
Beta Was this translation helpful? Give feedback.
-
That could happen via source generators and perhaps dotnet/roslyn#162 to not bloat production assembly with unused code. The definitive answer to this problem would be variadic generics dotnet/roslyn#5058 which is unlikely to happen anytime soon. Rel: https://github.com/dotnet/corefx/issues/16010 and https://github.com/dotnet/corefx/issues/16011 |
Beta Was this translation helpful? Give feedback.
-
I'd love compiler support for awaiting all in a tuple of tasklikes of heterogenous types. |
Beta Was this translation helpful? Give feedback.
-
We can accept length-dependant operations as a parameter, and generalize the rest, static TaskAwaiter<TResultTuple> GetAwaiter<TTuple, TResultTuple>(
this TTuple tasks,
Func<TTuple, Task[]> splat,
Func<TTuple, TResultTuple> getResults)
{
return Task.WhenAll(splat(tasks)).ContinueWith(_ => getResults(tasks)).GetAwaiter();
}
GetAwaiter((task1, task2),
tasks => new Task[] { tasks.task1, tasks.task2 },
tasks => (tasks.task1.Result, tasks.task2.Result); With "tuple splatting" (#424) we can write less code in the call-site,
As a result tuple element names of the returned task will be reserved. However, to be able to use static TaskAwaiter<T..> GetAwaiter<T..>(this Task<T>.. tasks)
=> Task.WhenAll(tasks..).ContinueWith(_ => tasks..Result).GetAwaiter(); which translates to, static TaskAwaiter<TResultTuple> GetAwaiter<TTuple, TResultTuple>(
this TTuple tasks,
[TupleSplat] Func<TTuple, Task[]> splat,
[TupleMemberSplat("Result")] Func<TTuple, TResultTuple> getResults)
{
return Task.WhenAll(splat(tasks)).ContinueWith(_ => getResults(tasks)).GetAwaiter();
} Attributes Then we can var (a, b, c) = await (task1, task2, task3); PS: The two operations can also be defined as extension methods over length-dependant tuples so you don't need to repeat every method for any given tuple length and just pass the result of those extensions. This does not work for delegates since we require |
Beta Was this translation helpful? Give feedback.
-
Fyi, added |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I put up a NuGet package based on your work let me know if you object or want me to change any of the metadata I wanted to use your code but I didn't want it in my main repository at work. https://www.nuget.org/packages/TaskTupleAwaiter/ In order to use it simply import the TakeTupleAwaiter package via a using statement. |
Beta Was this translation helpful? Give feedback.
-
@buvinghausen Very nice. I'm happy you went to that effort and added tests. As far as package copyright, it should match the copyright line in your license link. MIT is good. 👍 |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I also updated the NuGet package to now support both .NET Framework 4.5 & .NET Standard 1.0 to get the broadest coverage. link: NuGet |
Beta Was this translation helpful? Give feedback.
-
I updated the gist. The @buvinghausen Want to update the NuGet package? |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I am crazy busy at work right now but I will try to get to it during some downtime. I logged an issue against the repo so I don't lose sight of this. Thanks! |
Beta Was this translation helpful? Give feedback.
-
Hopefully buvinghausen/TaskTupleAwaiter#4 helps then. =) |
Beta Was this translation helpful? Give feedback.
-
I updated the gist and Brian merged buvinghausen/TaskTupleAwaiter#8. |
Beta Was this translation helpful? Give feedback.
-
I propose that the compiler will allow you to await multiple tasks in the form of a tuple, giving a tuple (of the same size) of results when execution of the method resumes.
Currently in C# it's a pain to await multiple tasks that return results. If all the async methods return
Task
it's fine, or all of them return the sameT
inTask<T>
then it's not that bad; you can get the results out of the resulting array:Example 1:
The above example compiles because I've called the same method twice, so I'm awaiting an array of
Task<int>
and everything is peachy.But what if the methods return different generic instantiations of
Task<T>
? Now it's not even possible to useTask.WhenAll
to actually return the data--one must use the bareTask
objects:Example 2:
This is clumsy and potentially error-prone, and always has been since
await
was introduced. But, now we have tuples! Wouldn't it be much better if the compiler would allow the await keyword to be followed by aValueTuple
ofTasks
?Example 3:
I think the above is much simpler and easier to read, much quicker to type, and the user is much less likely to accidentally introduce a bug when writing it.
In terms of the implementation of this feature, I'd assume that it's not too much work because Example 3 would compile to the exact same IL as Example 2. In fact, it might even be able to be done with a new type (say,
TupleTask
) that itself can be awaited and then all that's needed is support from the compiler to hide the call to theTupleTask
constructor (just as it does for tuples). Without the compiler support it might look like this:Beta Was this translation helpful? Give feedback.
All reactions