等待任务<服务的结果>在非泛型方法中使用反射

本文关键字:泛型方法 反射 任务 服务 结果 等待 | 更新日期: 2023-09-27 18:30:42

考虑以下情况:

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 ',但也许我在这里错过了一个边缘情况。除此之外,我该如何完成这项工作?谢谢!

等待任务<服务的结果>在非泛型方法中使用反射

根据我的评论:

与接口不同,具体类型(如 Task<TResult>)不能是协变的。请参阅为什么任务不是协变?。所以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;

我需要在城堡拦截器中获取任务结果。这段代码对我有用:

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

我的选项具有扩展方法的形式。它接受任何Task实例,并返回具有一般任务的实际结果或非通用任务的实际结果null Task<object?>

internal static class TaskExtensions
{
    public static async Task<object?> ToGenericTaskAsync(this Task task)
    {
        await task;
        var taskType = task.GetType();
        if (!taskType.IsGenericType 
            || taskType.GetGenericTypeDefinition() != typeof(Task<>)
            || taskType.GetGenericArguments()[0] == Type.GetType("System.Threading.Tasks.VoidTaskResult"))
        {
            return null;
        }
        return task
            .GetType()
            .GetProperty(nameof(Task<object>.Result), BindingFlags.Instance | BindingFlags.Public)!
            .GetValue(task);
    }
}

用法:

Task<object?> t1 = await Task.CompletedTask.ToGenericTaskAsync();
Task<object?> t2 = await Task.FromResult<int>(5).ToGenericTaskAsync();

.NET 小提琴中的示例

上面的解决方案确实对我有帮助。 我对@Lukazoid解决方案进行了小调整...

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

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