将Task <tresult>中的TResult转换为System.Object </tresult>

时间:2014-02-16 00:16:42

标签: c# asynchronous task

我正在加载一个程序集并调用一个静态方法,该方法将使用MethodInfo.Invoke()通过反射创建一个类型为“MyClass1”的新对象(此类型在运行时指定)。当方法是普通同步方法时,这可以正常工作。但是,被调用的方法是一个异步方法,它返回Task&lt; MyClass1&gt;,它将用于使用task.Result检索结果。

理想情况下,我应该在任务中使用MyClass1作为TResult,但是类型仅在运行时确定,所以我不能这样做。我正在寻找一种方法来获得任务和结果。我试图将TResult转换为System.Object并将该类作为通用对象。以下是我为此目的使用的代码。

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

以下是通过反射调用的方法(测试代码)。这个

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

不幸的是,TResult的转换导致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]'.

如何在任务&lt;&gt;中投射TResult?到一个通用对象并使用task.Result获取结果?我很感激任何帮助解决这个问题。

3 个答案:

答案 0 :(得分:18)

您无法将Task<T>投射到Task<object>,因为Task<T>不是covariant(它也不是逆变的)。最简单的解决方案是使用更多的反射:

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

这是缓慢而低效的,但如果不经常执行此代码,则可以使用。顺便说一句,如果要阻止等待其结果,使用异步MakeMyClass1方法会有什么用?

答案 1 :(得分:8)

另一种可能性是为此目的编写扩展方法:

    public static Task<object> Convert<T>(this Task<T> task)
    {
        TaskCompletionSource<object> res = new TaskCompletionSource<object>();

        return task.ContinueWith(t =>
        {
            if (t.IsCanceled)
            {
                res.TrySetCanceled();
            }
            else if (t.IsFaulted)
            {
                res.TrySetException(t.Exception);
            }
            else
            {
                res.TrySetResult(t.Result);
            }
            return res.Task;
        }
        , TaskContinuationOptions.ExecuteSynchronously).Unwrap();
    }

它是无阻塞解决方案,将保留任务的原始状态/异常。

答案 2 :(得分:6)

作为对已接受答案的增强,您可以通过等待Task之间来避免阻止:

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

当然,只有当调用堆栈中的所有方法都标记为async并且您在任何地方使用await而不是.Result时,这才会正确异步。