Cannot convert type 'Task<Derived>' to 'Task<Interface>'
I have the following function with a delegate parameter that accepts a type of one interface and returns a task of another.
public void Bar(Func<IMessage, Task<IResult>> func)
{
throw new NotImplementedException();
}
I also have a function with a parameter as an instance of IMessage
and returns a Task. Message
and Result
are implementations of IMessage
and IResult
respectively.
private Task<Result> DoSomething(Message m) { return new Task<Result>(() => new Result()); }
I receive an error when I pass DoSomething into Bar.
Bar(m => DoSomething((Message)m));
// Cannot convert type 'Task<Result>' to 'Task<IResult>'
Why won't Result
implicitly convert into IResult
?
I would imagine it's an issue with covariance. However, in this case, Result
implements IResult
. I've also tried to solve the covariance issue by creating an interface and marking TResult
as covariant.
public interface IFoo<TMessage, out TResult>
{
void Bar(Func<TMessage, Task<TResult>> func);
}
But I get the error:
Invalid variance: The type parameter 'TResult' must be invariantly valid on
IFoo<TMessage, TResult>.Bar(Func<TMessage, Task<TResult>>)
. 'TResult' is covariant.
Now I'm stuck. I know I have an issue with covariance but I'm not sure how to solve it. Any ideas?
Edit: This question is specific to Tasks. I ran into this problem by implementing async await
in my application. I came across this generic implementation and added a Task
. Others may have the same issues during this type of conversion.
Solution: Here's the solution based on the answers below:
Func<Task<Result>, Task<IResult>> convert = async m => await m;
Bar(m => convert(DoSomething((Message)m)));
Solution 1:
C# does not allow variance on classes, only interfaces and delegates that are parameterized with reference types. Task<T>
is a class.
This is somewhat unfortunate, as Task<T>
is one of those rare classes that could be made safely covariant.
However it is easy enough to convert a Task<Derived>
to a Task<Base>
. Just make a helper method / lambda that takes a Task<Derived>
and returns Task<Base>
, await the passed-in task, and return the value cast to Base
. The C# compiler will take care of the rest. Of course you lose referential identity, but you weren't ever going to get that with a class.
Solution 2:
It seems like there's got to be a cleaner way of doing this, but it is possible to create a wrapping task of the correct type. I introduced a new function called GeneralizeTask()
.
Task<TBase> GeneralizeTask<TBase, TDerived>(Task<TDerived> task)
where TDerived : TBase
{
var newTask = new Task<TBase>(() => {
if (task.Status == TaskStatus.Created) task.Start();
task.Wait();
return (TBase)task.Result;
});
return newTask;
}
Edit:
As @EricLippert points out, this can be simplified significantly. I first tried to find such a way to implement this method, but couldn't find one that compiled. As it turned out, the real solution was even simpler than I imagined.
async Task<TBase> GeneralizeTask<TBase, TDerived>(Task<TDerived> task)
where TDerived : TBase
{
return (TBase) await task;
}
You can then invoke Bar()
like this.
Bar(m => GeneralizeTask<IResult, Result>(DoSomething((Message)m)));
Solution 3:
I am using another version of GeneralizeTask, which is stated by @Recursive, on Asp Net Core Framework. Here it is:
public static Task<TBase> GeneralizeTask<TDerived, TBase>(this Task<TDerived> task, CancellationToken cancellationToken = default)
where TBase : class
where TDerived : TBase
{
var result = task.ContinueWith(t => (TBase)t.Result, cancellationToken);
return result;
}