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

时间:2016-01-15 15:50:41

标签: 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;
    }
}

调用new Main().Create(typeof(B))将失败并显示

  

无法将“System.Threading.Tasks.Task[B]”类型的对象转换为   输入“System.Threading.Tasks.Task[A]

我不太明白,因为在这种情况下,通用Create<T>方法只能返回Task<T>,其中T始终来自“A”,但也许我在这里错过了一个边缘案例。 除此之外,我怎样才能做到这一点?谢谢!

3 个答案:

答案 0 :(得分:20)

根据我的评论:

  

与接口不同,Task<TResult>等具体类型不能协变。见Why is Task not co-variant?。因此,无法将Task<B>分配给Task<A>

我能想到的最佳解决方案是使用基础类型Task来执行await,如下所示:

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

然后您可以使用反射来获取Result

的值
var resultProperty = typeof(Task<>).MakeGenericType(type).GetProperty("Result");
A a = (A)resultProperty.GetValue(task);
return a.Id;

答案 1 :(得分:0)

我需要在 Castle Interceptor 中获取任务结果。此代码适用于我:

if (invocation.ReturnValue is Task task)
{
    task.Wait();

    var result = invocation.ReturnValue.GetType().GetProperty("Result").GetValue(task);
    _cacheProvider.Set(_key, result, _duration);
}

答案 2 :(得分:-1)

上述解决方案确实对我有所帮助。我对@Lukazoid解决方案做了一个小调整...

var resultProperty = typeof(Task<>).MakeGenericType(type).GetProperty("Result");
A a = (A)resultProperty.GetValue(task);

dynamic a = task.GetType().GetProperty("Result")?.GetValue(task);