Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting TResult in Task<TResult> to System.Object

I am loading an assembly and calling a static method that will create a new object of type “MyClass1” (this type is specified at runtime) through reflection using MethodInfo.Invoke(). This works fine when the method is a normal sync method. However, the method being called is an async method which returns Task<MyClass1>, which will be used to retrieve the result using task.Result.

Ideally I should be using MyClass1 as TResult in the task, but the type is determined only at runtime so I can't do that. I am looking for a way to get the task and the result. I am trying to cast the TResult to System.Object and get the class as a generic Object. The following is the code I am using for this purpose.

public static void LoadAssembly()
{
    // Calling static async method directly works fine
    Task<MyClass1> task1 = MyClass1.MakeMyClass1();
    MyClass1 myClass1 = task1.Result;

    // Calling static async method through reflection through exception.
    Assembly assembly = Assembly.LoadFrom(dllName);
    Type type = assembly.GetType("AsyncDll.MyClass1");
    var types = assembly.GetTypes(); 
    MethodInfo[] methodInfos = types[0].GetMethods(BindingFlags.Public | BindingFlags.Static);
    Type myClassType = types[0];
    MethodInfo mi = myClassType.GetMethod("MakeMyClass1");
    Object obj = Activator.CreateInstance(mi.ReflectedType);
    Task<Object> task = (Task<Object>)mi.Invoke(obj, null); // Exception occurs here.
    Object result = task.Result;
}

Following is the method (test code) being called through reflection. This

public class MyClass1
{
    public static async Task<MyClass1> MakeMyClass1()
    {
        MyClass1 newObject = null;
        await Task.Run(() =>
        {
            newObject = new MyClass1();
        });
        return newObject;
    }
    ...
}

Unfortunately, the casting of TResult is causing System.InvalidCastException.

An unhandled exception of type 'System.InvalidCastException' occurred in Test.exe

Additional information: Unable to cast object of type 'System.Threading.Tasks.Task`1[MyClass1]' to type 'System.Threading.Tasks.Task`1[System.Object]'.

How can I cast the TResult in Task<> to a generic object and get the result using task.Result? I would appreciate any help resolving this issue.

like image 250
mtcup Avatar asked Feb 16 '14 00:02

mtcup


People also ask

How do you return a value from a Task?

You want to return a task result to the client. Set the task's Result property with your user defined result object. The task's Result property is serialized and returned back to the client.

What should I return from Task C#?

The recommended return type of an asynchronous method in C# is Task. You should return Task<T> if you would like to write an asynchronous method that returns a value. If you would like to write an event handler, you can return void instead. Until C# 7.0 an asynchronous method could return Task, Task<T>, or void.

What is the data type of the result property of the Task class?

The Task<T> class has a Result property of type T that contains whatever you pass back with return statements in the method. The caller generally retrieves what's stored in the Result property implicitly, through the use of await .

What is ValueTask C#?

From the time the Task Parallel Library (TPL) was introduced, developers have been using the Task class to write asynchronous code. The ValueTask struct is a new type introduced in C# 7.0 that provides a way to work with time-sensitive tasks with less resource consumption (memory, CPU, etc.) overhead.


2 Answers

As an enhancement to the accepted answer, you can avoid the blocking by awaiting the Task in between:

var task = (Task)mi.Invoke(obj, null);
await task;
var result = task.GetType().GetProperty("Result").GetValue(task);

Of course this will be properly asynchronous only if all methods up the call stack are marked async and you're using await instead of .Result everywhere.

like image 109
Todd Menier Avatar answered Oct 16 '22 22:10

Todd Menier


You cannot cast Task<T> to Task<object>, because Task<T> is not covariant (it's not contravariant, either). The simplest solution would be to use some more reflection:

var task   = (Task) mi.Invoke (obj, null) ;
var result = task.GetType ().GetProperty ("Result").GetValue (task) ;

This is slow and inefficient, but usable if this code is not executed often. As an aside, what is the use of having an asynchronous MakeMyClass1 method if you are going to block waiting for its result?

like image 37
Anton Tykhyy Avatar answered Oct 17 '22 00:10

Anton Tykhyy