在非泛型方法中使用反射等待Task <TDerived>的结果

Raz*_*zie 9 c# generics reflection async-await

考虑以下情况:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

呼叫new Main().Create(typeof(B))将失败

无法将' System.Threading.Tasks.Task[B]'类型的对象强制转换为' System.Threading.Tasks.Task[A]'

我不太明白,因为在这种情况下,Generic Create<T>方法只返回始终从' ' 派生的Task<T>where ,但也许我在这里缺少一个边缘情况.除此之外,我怎样才能做到这一点?谢谢!TA

Luk*_*oid 21

根据我的评论:

与接口不同,具体类型如Task<TResult>不能协变.请参阅为什么任务不是共变体?.所以Task<B>不能分配到Task<A>.

我能想到的最好的解决方案是使用底层类型Task来执行await类似的操作:

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

然后你可以使用反射来获取:的值Result:

var resultProperty = typeof(Task<>).MakeGenericType(type).GetProperty("Result");
A a = (A)resultProperty.GetValue(task);
return a.Id;
Run Code Online (Sandbox Code Playgroud)

  • task.GetType()很可能总是很好,但是如果结果Task是派生的类型,而它覆盖了Results,则会抛出AmbiguousMatchException。这样的事情永远不会发生,因为我更明确地说明了在哪里可以找到`Result`属性。 (2认同)