How to call a generic async method using reflection

后端 未结 4 991
死守一世寂寞
死守一世寂寞 2020-12-13 13:42
public interface IBar {
}
public class Bar : IBar {
}
public class Bar2 : IBar {
}
public interface IFoo {
  Task Get(T o) where T : IBar;
}
public         


        
4条回答
  •  轮回少年
    2020-12-13 14:21

    Because Task derives from Task you can await on just that, once the task is awaited you can use reflection to safely access the .Result property via reflection.

    Once you have the result you will either need to store it in a IBar and use the methods and properties on that or cast to the specific type after testing to use the type specific methods.

    Here is a full MCVE of it

    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 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 Get(T o) where T : IBar;
            }
            public class Foo : IFoo
            {
                public async Task Get(T o) where T : IBar
                {
                    await Task.Delay(100);
                    return o;
                }
            }
        }
    }
    
    
    

    UPDATE: Here is a extension method to simplify the process

    public static class ExtensionMethods
    {
        public static async Task 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);
        }
    }
    
    
    

    This turns CallGetByReflection in to

    private static Task 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});
    }
    
    
    

    UPDATE 2: Here is a new extension method that works with any awaitable type instead of only tasks by using dynamic and GetAwaiter()

    public static class ExtensionMethods
    {
        public static async Task InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
        {
            dynamic awaitable = @this.Invoke(obj, parameters);
            await awaitable;
            return awaitable.GetAwaiter().GetResult();
        }
    }
    
        

    提交回复
    热议问题