I’m trying to return a specifically typed value from a generic function (GenericGetter).
When I try to simply return the typed Task result from GenericGetter, the compiler is showing me the following error:
Cannot convert expression type 'System.Threading.Tasks.Task<Example.ChildClass>'
to return type 'System.Threading.Tasks.Task<Example.BaseClass>'
However, if I make the function that contains the GenericGetter call async, and I return the awaited value instead, the compiler doesn’t mind. It compiles, it works, but to me it seems the added async / await are redundant.
Why does GetChildClass not compile, while GetChildClassAsync does?
Here’s my example:
namespace Example
{
public class BaseClass {}
public class ChildClass : BaseClass {}
public class MyExample
{
private async Task Main()
{
var foo = await GetChildClass().ConfigureAwait(false);
var bar = await GetChildClassAsync().ConfigureAwait(false);
}
private Task<BaseClass> GetChildClass() =>
GenericGetter<ChildClass>();
private async Task<BaseClass> GetChildClassAsync() =>
await GenericGetter<ChildClass>().ConfigureAwait(false);
private Task<T> GenericGetter<T>()
where T : BaseClass =>
Task.FromResult<T>(null);
}
}
>Solution :
In GetChildClass, you’re trying to convert a Task<ChildClass> into Task<BaseClass> – that doesn’t work, because Task<T> is invariant (as are all classes; only interfaces and delegates can be covariant or contravariant).
In GetChildClassAsync, you’re trying to convert a ChildClass into a BaseClass (which is allowed by normal inheritance and conversions) – and the C# compiler then does the wrapping into a Task<BaseClass>, so that’s fine.
I see why it appears to be redundant, and there are potentially more efficient ways that it could be done (it would be nice to be able to create a Task<SomeBaseType> from Task<SomeChildType> using a framework method/constructor) but I’d suggest just living with it.