As the title states, I'm curious if it's possible to convert from Task<List<Derived>>
to Task<List<Base>>
?
No. Suppose it were legal, and see what goes wrong:
Task<List<Giraffe>> t1 = GetGiraffesAsync();
Task<List<Animal>> t2 = t1; // Suppose this were legal
(await t2).Add(new Fish());
And now we have a fish in a list of giraffes.
One of those three lines has to be illegal. Plainly it cannot be the first, since GetGiraffesAsync
returns Task<List<Giraffe>>
. Plainly it cannot be the last, since await t2
produces a List<Animal>
, and a fish is an animal. Therefore it must be the middle line that is illegal.
Now, you could do this:
async Task<List<Animal>> ConvertAsync(Task<List<Giraffe>> t) =>
(await t).Select(g => (Animal)g).ToList();
Or
async Task<List<Animal>> ConvertASync(Task<List<Giraffe>> t) =>
(await t).Cast<Animal>().ToList();
or
async Task<List<Animal>> ConvertAsync(Task<List<Giraffe>> t) =>
(await t).OfType<Animal>().ToList();
If you wanted to make it generic you could do
async Task<<List>Animal> ConvertAsync<T>(Task<List<T>> t) where T : Animal =>
(await t).OfType<Animal>().ToList();
and now it works with giraffes and fish and tigers and so on.
That is, you can asynchronously wait for the original task to finish, and when it is done, you can create a new list of animals from your list of giraffes. That's perfectly legal. You now have two lists, one of giraffes and one of animals.
Or, you could do this:
async Task<IEnumerable<Animal>> ConvertAsync(Task<List<Giraffe>> t) =>
await t;
since List<Giraffe>
is convertible to IEnumerable<Animal>
.
I would be inclined to write it as an extension method.
static class Extensions {
public static Task<List<Animal>> ConvertAsync<T>(
this Task<List<T>> t) where T : Animal {
return (await t).OfType<Animal>().ToList();
}
}
And now your program fragment is:
var tasks = new List<Task<List<Animal>>>
{
GetGiraffesAsync().ConvertAsync(),
GetTigersAsync().ConvertAsync()
};
>` to `Task
– Andrew May 08 '18 at 15:53>`"