如何将任务<tderived>转换为任务<tbase>?</tbase> </tderived>

时间:2013-03-20 17:04:47

标签: c# task async-await

由于C#的Task是一个类,你显然无法将Task<TDerived>转换为Task<TBase>

但是,你可以这样做:

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

我是否可以调用一个静态任务方法来获取Task<TDerived>实例,该实例基本上只指向底层任务并转换结果?我喜欢这样的东西:

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

这种方法存在吗?是否仅为此目的使用异步方法有任何开销?

3 个答案:

答案 0 :(得分:39)

  

这种方法存在吗?

没有

  

仅为此目的使用异步方法是否有任何开销?

是。但这是最简单的解决方案。

请注意,更通用的方法是Task的扩展方法,例如Then。 Stephen Toub探讨了这个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;
}

答案 1 :(得分:16)

  

这种方法存在吗?是否仅为此目的使用异步方法有任何开销?

没有内置方法,这确实会导致开销。

“最轻的”替代方案是使用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;
}

答案 2 :(得分:1)

你可以试试这个: task.ContinueWith<TDerived>( t => (TDerived)t.Result);