2016-01-01 92 views
4

如何將Task<object>轉換爲Task<T>其中T未知?將任務<object>轉換爲任務<T>其中T未知

例如 Task<object> to Task<int>

interface IAsyncActions 
{ 
    Task<int> GetValueAsync(); 
} 

class Proxy : RealProxy 
{ 
    public Proxy() : base(typeof(IAsyncActions)) { } 

    public override IMessage Invoke(IMessage msg) 
    { 
     var call = (IMethodCallMessage)msg; 
     var method = (MethodInfo)call.MethodBase; 
     if (method.ReturnType.IsSubclassOf(typeof(Task))) 
     { 
      // return type is Task<T> 
      Task taskResult = AsyncMethod2(call); 
      return new ReturnMessage(taskResult, null, 0, call.LogicalCallContext, call); // InvalidCastException if taskResult not a Task<int> 
     } 
     else 
     { 
      // ... 
      return null; 
     } 
    } 

    static void Main() 
    { 
     Proxy p = new Proxy(); 
     IAsyncActions tProxy = (IAsyncActions)p.GetTransparentProxy(); 
     int result = tProxy.GetValueAsync().Result; // InvalidCastException 
    } 

    // This method works fine 
    Task AsyncMethod(IMethodCallMessage call) 
    { 
     Task<int> task = Task.FromResult(1234); 
     return task; 
    } 

    // This method does not work 
    Task AsyncMethod2(IMethodCallMessage call) 
    { 
     Type taskReturnType = ((MethodInfo)call.MethodBase).ReturnType; // Task<int> 

     Task<object> result = Task.FromResult<object>(1234); 

     // converting result to taskReturnType 
     // ... 
     // 

     return result; 
    } 
} 

我找到了一個解決方案,但它是相當昂貴:

Task AsyncMethod2(IMethodCallMessage call, MethodInfo method) 
{ 
    PropertyInfo resultProp = method.ReturnType.GetProperty("Result"); 
    Type taskResultType = resultProp.PropertyType; 
    Type tcs = typeof(TaskCompletionSource<>); 
    Type[] typeArgs = { taskResultType }; 
    Type genericTcs = tcs.MakeGenericType(typeArgs); 
    var taskProperty = genericTcs.GetProperty("Task"); 
    object tcsInstance = Activator.CreateInstance(genericTcs); 
    MethodInfo setResult = genericTcs.GetMethod("SetResult"); 
    MethodInfo setException = genericTcs.GetMethod("SetException", new Type[] { typeof(IEnumerable<Exception>)}); 
    var setEx = (Action< IEnumerable<Exception>>)setException.CreateDelegate(typeof(Action<IEnumerable<Exception>>), tcsInstance); 
    Task task = (Task)taskProperty.GetValue(tcsInstance); 

    Task<object> result = new Task<object>(delegate 
    { 
     //throw new InvalidOperationException("qwerty"); 
     return 1234; 
    }); 

    result.Start(); 

    result.ContinueWith(x => 
    { 
     var args = new object[] { x.Result }; 
     setResult.Invoke(tcsInstance, args); 
    }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion); 

    result.ContinueWith(x => 
    { 
     setEx(x.Exception.InnerExceptions); 
    }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted); 

    return task; 
} 
+0

點是代碼必須被異步執行,這就是爲什麼我沒有1234的價值,同時任務沒有完成 – Miles

+0

我幾乎可以肯定的是https://msdn.microsoft.com/en-us /magazine/dn574805.aspx(Unity的異步攔截器)包含您正在查找的確切代碼。 –

+0

這很難理解,但這真的是我在找的,謝謝 – Miles

回答

8

創建此方法:

public async static Task<T> Convert<T>(Task<object> task) 
{ 
    var result = await task; 

    return (T) result; 
} 

然後你就可以這樣做這個(這是必需的,因爲T只在運行時已知):

//Assuming that variable "task" is referencing the Task<object> 

Type taskReturnType = ((MethodInfo) call.MethodBase).ReturnType; //e.g. Task<int> 

var type = taskReturnType.GetGenericArguments()[0]; //get the result type, e.g. int 

var convert_method = this.GetType().GetMethod("Convert").MakeGenericMethod(type); //Get the closed version of the Convert method, e.g. Convert<int> 

var result = convert_method.Invoke(null, new object[] {task}); //Call the convert method and return the generic Task, e.g. Task<int> 
+1

這不適合我。我得到一個異常不能'任務'到'任務'。使用這種方法作爲靈感,我創建了一個新的:'public static Task Convert (T value) { return Task.FromResult ((T)value); }' –