如何使用反射调用通用异步方法

时间:2016-09-24 09:51:19

标签: c# reflection async-await

148    void commitAndContinueAsRead() {
149            nativeCommitAndContinueAsRead(nativePtr);
150    }

然后我可以使用反射调用此方法:

public interface IBar {
}
public class Bar : IBar {
}
public class Bar2 : IBar {
}
public interface IFoo {
  Task<T> Get<T>(T o) where T : IBar;
}
public class Foo : IFoo {
  public async Task<T> Get<T>(T o) where T : IBar {
    ...
  }
}

我如何等待var method = typeof(IFoo).GetMethod(nameof(IFoo.Get)); var generic = method.MakeGenericMethod(bar2.GetType()); var task = generic.Invoke(foo, new [] { bar2 }); ?以及如何将其投射到Task

4 个答案:

答案 0 :(得分:29)

因为Task<T>派生自Task,您可以等待,只要等待任务,您就可以使用反射通过反射安全地访问.Result属性。

获得结果后,您需要将其存储在IBar中并使用其上的方法和属性,或者在测试后使用特定类型的方法强制转换为特定类型。

这是一个完整的MCVE

using System;
using System.Reflection;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Test().Wait();
            Console.ReadLine();
        }

        static async Task Test()
        {
            var foo = new Foo();
            var bar2 = new Bar2();

            object resultObject = await CallGetByReflection(foo, bar2);

            IBar result = (IBar)resultObject;
            result.WriteOut();

            //or

            if (resultObject is Bar)
            {
                ((Bar)resultObject).Something();
            }
            else if (resultObject is Bar2)
            {
                ((Bar2)resultObject).SomethingElse();
            }
        }

        private static async Task<object> CallGetByReflection(IFoo foo, IBar bar)
        {
            var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
            var generic = method.MakeGenericMethod(bar.GetType());
            var task = (Task) generic.Invoke(foo, new[] {bar});

            await task.ConfigureAwait(false);

            var resultProperty = task.GetType().GetProperty("Result");
            return resultProperty.GetValue(task);
        }

        public interface IBar
        {
            void WriteOut();
        }
        public class Bar : IBar
        {
            public void Something()
            {
                Console.WriteLine("Something");
            }
            public void WriteOut()
            {
                Console.WriteLine(nameof(Bar));
            }
        }
        public class Bar2 : IBar
        {
            public void SomethingElse()
            {
                Console.WriteLine("SomethingElse");
            }
            public void WriteOut()
            {
                Console.WriteLine(nameof(Bar2));
            }
        }
        public interface IFoo
        {
            Task<T> Get<T>(T o) where T : IBar;
        }
        public class Foo : IFoo
        {
            public async Task<T> Get<T>(T o) where T : IBar
            {
                await Task.Delay(100);
                return o;
            }
        }
    }
}

UPDATE :这是一个简化流程的扩展方法

public static class ExtensionMethods
{
    public static async Task<object> InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
    {
        var task = (Task)@this.Invoke(obj, parameters);
        await task.ConfigureAwait(false);
        var resultProperty = task.GetType().GetProperty("Result");
        return resultProperty.GetValue(task);
    }
}

这会将CallGetByReflection转换为

private static Task<object> CallGetByReflection(IFoo foo, IBar bar)
{
    var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
    var generic = method.MakeGenericMethod(bar.GetType());
    return generic.InvokeAsync(foo, new[] {bar});
}

更新2 :这是一种新的扩展方法,可以使用dynamicGetAwaiter()

处理任何等待类型而非仅任务
public static class ExtensionMethods
{
    public static async Task<object> InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
    {
        dynamic awaitable = @this.Invoke(obj, parameters);
        await awaitable;
        return awaitable.GetAwaiter().GetResult();
    }
}

答案 1 :(得分:3)

根据您的示例,您知道编译时返回对象的类型 - &gt; IFoo,因此您可以使用普通投射(IFoo)

var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(typeof(IBar));
var task = (Task<IBar>)generic.Invoke(foo, new [] { bar2 });

IBar result = await task;

如果您在编译时不知道某个类型,那么只需使用dynamic关键字

var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(bar2.GetType());
dynamic task = generic.Invoke(foo, new [] { bar2 });

IBar result = await task;

但是如果任务的类型在运行时不是Task<iFoo> - 将抛出异常 如果你需要具体的IBar那么

var concreteResult = Convert.ChangeType(result, bar2.GetType()); 

答案 2 :(得分:2)

在@ScottChamberlain answer之上(这很不错),我建议对InvokeAsync方法的一个小改进,以返回Task<T>而不是Task<object>。除此之外,第二种方法返回Task会很有用,InvokeAsync不支持该方法。

using System.Threading.Tasks;

namespace System.Reflection
{
    public static class MethodInfoExtensions
    {
        public static async Task<T> InvokeAsync<T>(this MethodInfo methodInfo, object obj, params object[] parameters)
        {
            dynamic awaitable = methodInfo.Invoke(obj, parameters);
            await awaitable;
            return (T)awaitable.GetAwaiter().GetResult();
        }

        public static async Task InvokeAsync(this MethodInfo methodInfo, object obj, params object[] parameters)
        {
            dynamic awaitable = methodInfo.Invoke(obj, parameters);
            await awaitable;
        }
    }
}

答案 3 :(得分:0)

您可以使用“ result”属性来避免使用“ await”关键字,并且不应在方法中取消对“ async”的使用。

var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(typeof(IBar));
var task = (Task<IBar>)generic.Invoke(foo, new [] { bar2 });

var resultProperty = task.GetProperty("Result");
var result = resultProperty.GetValue(task);
var convertedResult = Convert.ChangeType(result, bar2.GetType());