c# reflection async-await

c# - Cómo llamar a un método asíncrono genérico utilizando la reflexión.



reflection async-await (3)

Debido a que la Task<T> deriva de la Task , puede esperar solo eso, una vez que se espera la tarea, puede usar la reflexión para acceder de forma segura a la propiedad .Result través de la reflexión.

Una vez que tenga el resultado, deberá almacenarlo en un IBar y usar los métodos y las propiedades en él o convertirlo al tipo específico después de la prueba para usar los métodos específicos del tipo.

Aquí hay un completo MCVE de ello.

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; } } } }

ACTUALIZACIÓN : Aquí hay un método de extensión para simplificar el proceso

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); } }

Esto convierte a CallGetByReflection en

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}); }

ACTUALIZACIÓN 2 : Aquí hay un nuevo método de extensión que funciona con cualquier tipo de espera en lugar de solo tareas mediante el uso de dynamic y 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(); } }

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 { ... } }

Entonces puedo llamar a este método usando la reflexión:

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

¿Cómo espero en esta Task ? y ¿Cómo lo Task<bar2.GetType()> en la Task<bar2.GetType()> ?


En función de su ejemplo, sabe el tipo de objeto devuelto en tiempo de compilación -> IFoo , por lo que puede usar la conversión normal (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;

Si no conoce un tipo en tiempo de compilación, simplemente use dynamic palabra clave 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;

Pero si el tipo de tarea no es una Task<iFoo> en tiempo de ejecución, se lanzará una excepción
Y si necesitas un tipo concreto de IBar entonces

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


puede utilizar la propiedad "result" para evitar la palabra clave "await" y no debe decalre "async" en el método.

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());