Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Await the result of Task<TDerived> using reflection in a non-generic method

Consider the following case:

class A
{
    public int Id;
}

class B : A
{

}

class Main
{
    public async Task<int> Create(Type type)
    {
        MethodInfo method = this.GetType().GetMethod("Create", new Type[] { typeof(string) }).MakeGenericMethod(new Type[] { type });
        A a = await (Task<A>)method.Invoke(this, new object[] { "humpf" });
        return a.Id;
    }

    public async Task<T> Create<T>(string name) where T : A
    {
        T t = await Foo.Bar<T>(name);
        return t;
    }
}

Calling new Main().Create(typeof(B)) will fail with a

Unable to cast object of type 'System.Threading.Tasks.Task[B]' to type 'System.Threading.Tasks.Task[A]'

I don't quite understand because in this case, the Generic Create<T> method can only return a Task<T> where T is always derived from 'A', but maybe I'm missing an edge case here. Besides that, how can I make this work? Thanks!

like image 705
Razzie Avatar asked Jan 15 '16 15:01

Razzie


1 Answers

As per my comment:

Unlike interfaces, concrete types such as Task<TResult> cannot be covariant. See Why is Task not co-variant?. So Task<B> cannot be assigned to a Task<A>.

The best solution I can think of is to use the underlying type Task to perform the await like so:

var task = (Task)method.Invoke(this, new object[] { "humpf" });
await task;

Then you can use reflection to get the value of the Result:

var resultProperty = typeof(Task<>).MakeGenericType(type).GetProperty("Result");
A a = (A)resultProperty.GetValue(task);
return a.Id;
like image 82
Lukazoid Avatar answered Oct 23 '22 18:10

Lukazoid