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

I can then call this method using reflection:

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

How do I await on this Task? and How do I cast it to Task<bar2.GetType()>?

user2321864
  • 2,207
  • 5
  • 25
  • 35
  • The answer is to cast it since it returns a object (see @Fabio). However you should make sure that `Foo` does inherit `IFoo`, if not you'll get an exception because `foo` (supplied at the `Invoke`) is the wrong type. – joell Sep 24 '16 at 10:21
  • Possible duplicate of [How to await an async private method invoked using reflection in WinRT?](http://stackoverflow.com/questions/14711585/how-to-await-an-async-private-method-invoked-using-reflection-in-winrt) – Fabio Sep 24 '16 at 20:07

5 Answers5

106

Because Task<T> 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<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: Here is a extension method to simplify the process

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

This turns CallGetByReflection in to

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: 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<object> InvokeAsync(this MethodInfo @this, object obj, params object[] parameters)
    {
        dynamic awaitable = @this.Invoke(obj, parameters);
        await awaitable;
        return awaitable.GetAwaiter().GetResult();
    }
}
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Compiler error CS0656: Der vom Compiler angeforderte Member "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create" fehlt. This points to the awaitable after "return" in the last example. – ygoe Jun 24 '18 at 18:52
  • 4
    @ygoe Add the package Microsoft.CSharp to your project – reads0520 Sep 09 '18 at 21:36
  • 1
    Please note that `.GetAwaiter().GetResult()` blocks the execution control itself. – Rzassar Dec 21 '20 at 16:12
  • 5
    @Rzassar No it will not, because we did `await awaitable;` the line before `return awaitable.GetAwaiter().GetResult();` we are guaranteed that the task is in a completed state, therefor `GetResult()` will be a non-blocking call because the result will be available. – Scott Chamberlain Dec 22 '20 at 20:58
  • a direct casts also works `var method = typeof(IFoo).GetMethod(nameof(IFoo.Get)); var generic = method.MakeGenericMethod(bar.GetType(); var result = await (dynamic)generic.Invoke(foo, parameters);` – Peter Aug 22 '21 at 12:51
  • @ScottChamberlain how would you recommend modifying your extension method to handle a method with no return object (i.e. public async Task Whatever())? – kman Sep 16 '22 at 15:10
  • 1
    @ScottChamberlain What do you think about changing the return type of your extension method to Task and returning @ this.ReturnType == typeof(Task) ? null : awaitable.GetAwaiter().GetResult(); – kman Sep 16 '22 at 16:18
19

Based on your example you know type of returned object at compile time -> IFoo, so you can use normal casting (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;

If you don't know a type at compile time, then use dynamic keyword

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;

But if type of the task not a Task<iFoo> at runtime - exception will be thrown
And if you need concrete type of IBar then

var concreteResult = Convert.ChangeType(result, bar2.GetType()); 
Fabio
  • 31,528
  • 4
  • 33
  • 72
  • `var task = (Task)generic.Invoke(foo, new[] { bar2 });` throws `InvalidCastException` Unable to cast object of type `'System.Threading.Tasks.Task'` to type `'System.Threading.Tasks.Task'` – user2321864 Sep 24 '16 at 14:48
  • 1
    `dynamic task = generic.Invoke(foo, new [] { bar2 }); IBar result = await task;` always returns `null` – user2321864 Sep 24 '16 at 14:52
  • 1
    @user2321864 instead of doing `method.MakeGenericMethod(bar2.GetType());` you need to do `method.MakeGenericMethod(typeof(IBar));` then the first example will work. `Task` is not covariant so you can't cast `Task` to `Task` – Scott Chamberlain Sep 24 '16 at 18:24
  • 1
    @user2321864, `dynamic task ...` have nothing to do with what is returned by `Get` method. I think you need check behavior of the `Get` method. – Fabio Sep 24 '16 at 21:51
7

On top of @ScottChamberlain answer (which is great ) I'll suggest a small improvement on InvokeAsync method to return Task<T> rather than Task<object>. Besides that it would be useful having a second method returning Task, which isn't supported by 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;
        }
    }
}
Marco Thomazini
  • 378
  • 3
  • 10
1

Here is an easy and simple sample code

object[] param = {null};
var method = await (Task<bool>)typeof(YourClassName).GetMethod("YourMethodName",System.Reflection.BidingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(InstanceOfYourClass, param);
David Buck
  • 3,752
  • 35
  • 31
  • 35
-2

you can use "result" property to avoid "await" keyword and should not decalre "async" in method.

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()); 
workingbird
  • 67
  • 1
  • 6