如何将Task <TDerived>转换为Task <TBase>?

Cha*_*ion 25 c# task async-await

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

但是,你可以这样做:

public async Task<TBase> Run() {
    return await MethodThatReturnsDerivedTask();
}
Run Code Online (Sandbox Code Playgroud)

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

public Task<TBase> Run() {
    return Task.FromDerived(MethodThatReturnsDerivedTask());
}
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*ary 40

这种方法存在吗?

没有.

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

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

请注意,更通用的方法是Task诸如此类的扩展方法Then.Stephen Toub 在博客文章中对此进行了探讨,最近我将其整合到AsyncEx中.

使用Then,你的代码看起来像:

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

另一种略微减少开销的方法是创建自己的方法TaskCompletionSource<TBase>并使用派生结果完成(TryCompleteFromCompletedTask在我的AsyncEx库中使用):

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

或(如果您不想依赖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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你使用`Result`,你将把所有异常包装在`AggregateException`中,如果原始任务在取消状态下完成,则完成出错.也就是说,有了更多的代码可以正确处理这些情况,你可以*使用`ContinueWith`制作一个有效的解决方案而不需要`TaskCompletionSource`. (4认同)
  • 嗨斯蒂芬!我认为这里缺少“TaskScheduler.Default”。 (2认同)

Ree*_*sey 19

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

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

"最轻的"替代方案是使用a 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;
}
Run Code Online (Sandbox Code Playgroud)