簡體   English   中英

如何使用反射調用通用異步方法

[英]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 :這是一個新的擴展方法,它通過使用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();
    }
}

根據您的示例,您在編譯時知道返回對象的類型 -> 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM