2013-03-20 96 views
23

由於C#的任務是一類,你顯然不能投下Task<TDerived>Task<TBase>如何將任務<TDerived>轉換成任務<TBase>?

但是,你可以這樣做:

public async Task<TBase> Run() { 
    return await MethodThatReturnsDerivedTask(); 
} 

是否有一個靜態任務的方法我可以打電話得到Task<TDerived>實例,它本質上只是指向底層任務和注塑結果呢?我想喜歡的東西:

public Task<TBase> Run() { 
    return Task.FromDerived(MethodThatReturnsDerivedTask()); 
} 

這樣的方法是否存在?僅僅爲此目的使用異步方法是否有任何開銷?

回答

36

難道這樣的方法存在嗎?

是否有任何開銷使用異步方法只用於此目的?

是的。但這是最簡單的解決方案。

請注意,更通用的方法是Task的擴展方法,如Then。斯蒂芬·圖博探索了這個in a blog post,我最近incorporated it into AsyncEx

使用Then,你的代碼看起來像:

public Task<TBase> Run() 
{ 
    return MethodThatReturnsDerivedTask().Then(x => (TBase)x); 
} 

另一種方法略少的開銷將創建自己的TaskCompletionSource<TBase>,並將它(在我的AsyncEx庫使用TryCompleteFromCompletedTask)與獲取結果完成:

public Task<TBase> Run() 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 
    MethodThatReturnsDerivedTask().ContinueWith(
     t => tcs.TryCompleteFromCompletedTask(t), 
     TaskContinuationOptions.ExecuteSynchronously); 
    return tcs.Task; 
} 

或者(如果你不想承擔AsyncEx的依賴):

public Task<TBase> Run() 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 
    MethodThatReturnsDerivedTask().ContinueWith(t => 
    { 
    if (t.IsFaulted) 
     tcs.TrySetException(t.Exception.InnerExceptions); 
    else if (t.IsCanceled) 
     tcs.TrySetCanceled(); 
    else 
     tcs.TrySetResult(t.Result); 
    }, TaskContinuationOptions.ExecuteSynchronously); 
    return tcs.Task; 
} 
+0

爲什麼不'返回MethodThatReturnsDerivedTask()ContinueWith(任務=>(TBASE)task.Result,TaskContinuationOptions .ExecuteSynchronously)'? – 2014-01-05 03:17:04

+3

如果你使用'Result',你會在'AggregateException'包裹任何異常,如果原始任務處於取消狀態完成完整的故障。這就是說,通過一些更多的代碼來正確處理這些情況,你可以使用不帶TaskCompletionSource的'ContinueWith'來製作工作解決方案。 – 2014-01-05 13:52:10

15

難道這樣的方法存在嗎?僅僅爲此目的使用異步方法是否有任何開銷?

對此沒有內置的方法,這會造成開銷。

的「重量最輕的」替代方法是使用TaskCompletionSource<T>爲此創造了新的任務。這可以通過一個擴展方法來完成,像這樣:

static Task<TBase> FromDerived<TBase, TDerived>(this Task<TDerived> task) where TDerived : TBase 
{ 
    var tcs = new TaskCompletionSource<TBase>(); 

    task.ContinueWith(t => tcs.SetResult(t.Result), TaskContinuationOptions.OnlyOnRanToCompletion); 
    task.ContinueWith(t => tcs.SetException(t.Exception.InnerExceptions), TaskContinuationOptions.OnlyOnFaulted); 
    task.ContinueWith(t => tcs.SetCanceled(), TaskContinuationOptions.OnlyOnCanceled); 

    return tcs.Task; 
} 
+1

考慮通過執行'SetException(t.Exception.InnerExceptions)'處理異常分支以避免'AggregateException'包裝。 – 2013-03-20 17:24:12

+0

@StephenCleary好建議 - 更新。 – 2013-03-20 17:30:56

+0

你的意思是使用'TaskContinuationOptions'而不是'TaskCompletionOptions'? – Lukazoid 2014-01-04 16:36:17

-1

你可以試試這個: task.ContinueWith<TDerived>(t => (TDerived)t.Result);

相關問題