简体   繁体   中英

Why can't I return a Task<IEnumerable<out T>> without making my method async and using await

Currently I have a method with the signature public Task<IEnumerable<Descriptor>> GetAllDescriptors() where Descriptor is a public class I created. I then have various tasks that I call within this method like so:

var allDescriptors = Task.WhenAll(GetMovieDescriptorsAsync, GetSongDescriptorsAsync);

Since GetMovieDescriptorsAsync & GetSongDescriptorsAsync both return Task<Descriptor> I know that the variable type of allDescriptors is Task<Descriptor[]> which is great. However if I try return allDescriptors I get the error:

Cannot implicitly convert type 'System.Threading.Tasks.Task<Descriptor[]>' to 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<Descriptor>>'

However, if I change the method signature to public async Task<IEnumerable<Descriptor>> GetAllDescriptors() and then I try return await allDescriptors it works and I get the desired <IEnumerable<CompletionInfo> in the calling function:

var descriptors = await GetAllDescriptors();

Why is that and how can I return the expected IEnumerable<Descriptor> without having to use async and await?

Task.WhenAll<T> returns a Task<T[]> , so in your case

Task<IEnumerable<Decsriptor>[]> allDescriptors = Task.WhenAll(GetMovieDescriptorsAsync, GetSongDescriptorsAsync);

allDescriptors is a Task<IEnumerable<Decsriptor>[]> , that is a Task wrapping an array of Enumerables of Descriptor.

First order of business would be getting the task result:

IEnumerable<Descriptor>[] taskResults = allDescriptors.Result;

Then merging each IEnumerable<Descriptor> in the array taskResults into a flat list:

IEnumerable<Descriptor> descriptors = taskResults.SelectMany(value => value);

The problem (and solution) you are describing is covariance . IEnumerable<out T> is a covariant interface, which means you can assign IEnumerable<string> into IEnumerable<object> .

However, Task<TResult> IS NOT covariant. That means you can't return a more specific object in place of a generic definition (if you've Task<object> , you can't return Task<string> ).

Now, adding async and await makes the compiler treat this a bit differently. Simplifying, async cancels out the Task declaration, so it returns the TResult . And if you have a method object Foo() , you can use return "" inside this method. Because the return type of a method is always covariant.

To summarize, async Task<TResult> is similar of TResult , so you can return more specific objects this way. However, Task<TResult> is a non-covariant type, so you have to return exactly the type specified.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM