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);
}
}
CodePudding user response:
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 generically covariant or contravariant - all arrays support covariance, although in an "interesting" way).
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.