2013-03-20 91 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);

相关问题