[英]How to call a generic async method using reflection
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
? 以及如何将其转换为Task<bar2.GetType()>
?
因为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;
}
}
}
}
更新:这是一个简化过程的扩展方法
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 :这是一个新的扩展方法,它通过使用dynamic
和GetAwaiter()
来处理任何可等待类型,而不仅仅是任务
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();
}
}
根据您的示例,您在编译时知道返回对象的类型 -> 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());
在@ScottChamberlain 的回答之上(这很棒😉)我会建议对InvokeAsync
方法进行一个小的改进以返回Task<T>
而不是Task<object>
。 除此之外,使用第二个方法返回Task
会很有用, InvokeAsync
不支持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;
}
}
}
这是一个简单而简单的示例代码
object[] param = {null};
var method = await (Task<bool>)typeof(YourClassName).GetMethod("YourMethodName",System.Reflection.BidingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(InstanceOfYourClass, param);
您可以使用“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());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.