Cannot convert type 'Task<Derived>' to 'Task<Interface>'

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.


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)));

Tags:

C#