web-dev-qa-db-ja.com

リフレクションを使用して一般的な非同期メソッドを呼び出す方法

_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()>にキャストしますか?

26
user2321864

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

PDATE 2dynamicGetAwaiter()を使用して、タスクだけでなく、待機可能な任意のタイプで機能する新しい拡張メソッドを次に示します

_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();
    }
}
_
34

あなたの例に基づいて、コンパイル時に返されるオブジェクトのタイプ-> 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()); 
4
Fabio

「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()); 
0
workingbird