jeffj23
jeffj23

Reputation: 171

Async MethodInfo Invoke - cast return type dynamically

I am attempting to call an awaitable method via MethodInfo invoke.
The MethodInfo.Invoke() returns type object that needs to be cast as a Task before I can await it. If I manually cast the return type with a generic parameter of Task, the process works correctly.

However, I have a number of these methods to call that have different return types. How do I handle the return type?

public class Repository
{
    public IEnumerable<Foo> GetListAsync(int criteria)
    {
        return new List<Foo> {new Foo {Bar = "1"}, new Foo {Bar = "2"}};
    }
}

public class Foo
{
    public string Bar { get; set; }
}

public class Precache
{
    public static void RegisterPreCache(Repository instanceObject)
    {

        var preCachedType = typeof (Repository);

        var preCachableMethods =
            preCachedType
                .GetMethods();

        foreach (var cachableMethod in preCachableMethods)
        {
            var resultTask =
                (Task<IEnumerable<Foo>>) cachableMethod.Invoke(instanceObject,
                    new object[]
                    {1});
            Task.Run(async () => await resultTask).Wait();
        }
    }
}

I can get the return type easily from cacheableMethod.ReturnType property. How can I do the cast dynamically? Without the cast, I cannot await the invocation.

I have tried casting simply as Task which of course does not work (EDIT: It does work). I have tried passing all of the parameters into a new generic method, but if I do not specify the generic type must be a Task (where T : Task), I get a compile time error that there is no definition for GetAwaiter.

private static void CallExpression<T>(T returnType, MethodInfo     
    cachableMethod, object instanceObject, object[] parameters) 
{
    var resultTask = (T) cachableMethod.Invoke(instanceObject, parameters);
    Task.Run(async () => await resultTask).Wait();
}

Thank you

Upvotes: 1

Views: 4260

Answers (2)

Serge Semenov
Serge Semenov

Reputation: 10052

The answer is already posted:

var resultTask = (Task)cachableMethod.Invoke(...)  

However, be aware:

  1. You can await on anything besides Task and Task<T>, so if your downstream method returns something that can be awaited on with an extension method, that approach will not work.
  2. The problem number 1 can be a problem in future C# 7 feature, where you can return a custom awaitable type in async method besides Task or Task<T>

Upvotes: 1

jeffj23
jeffj23

Reputation: 171

With many thanks to Jon Skeet, was able to determine that the issue was the fact that I was making the cast too specific.

var resultTask = (Task) cachableMethod.Invoke(...)  

allows the MethodInfo invocation to be awaited.

Upvotes: 6

Related Questions