it-swarm.com.de

So rufen Sie eine generische asynchrone Methode mit Reflektion auf

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

Ich kann diese Methode dann mit Reflektion aufrufen:

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

Wie kann ich auf diese Task warten? und wie verwende ich es in Task<bar2.GetType()>?

17
user2321864

Da Task<T> von Task abgeleitet ist, können Sie genau darauf warten. Sobald die Aufgabe abgewartet wird, können Sie mit Reflection sicher auf die .Result-Eigenschaft über Reflection zugreifen. 

Sobald Sie das Ergebnis erhalten haben, müssen Sie es entweder in einer IBar speichern und die entsprechenden Methoden und Eigenschaften verwenden oder nach dem Test in einen bestimmten Typ umwandeln, um die typspezifischen Methoden zu verwenden.

Hier ist eine vollständige MCVE davon

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: Hier ist eine Erweiterungsmethode, um den Prozess zu vereinfachen

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

Dadurch wird CallGetByReflection in umgestellt

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

UPDATE 2 : Hier ist eine neue Erweiterungsmethode, die mit jedem erwartbaren Typ anstelle von nur Tasks funktioniert, indem dynamic und GetAwaiter() verwendet werden.

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

Anhand Ihres Beispiels kennen Sie den Typ des zurückgegebenen Objekts zur Kompilierzeit -> IFoo, sodass Sie das normale Casting verwenden können (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;

Wenn Sie zum Zeitpunkt der Kompilierung keinen Typ kennen, verwenden Sie einfach das Schlüsselwort 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;

Wenn jedoch der Typ der Aufgabe zur Laufzeit kein Task<iFoo> ist, wird eine Ausnahme ausgelöst
Und wenn Sie einen konkreten Typ von IBar benötigen, dann

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

sie können die Eigenschaft "result" verwenden, um das Schlüsselwort "await" zu vermeiden, und Sie sollten in der Methode "async" nicht deklarieren.

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