26

由于 C# 的 Task 是一个类,因此您显然不能将 a 强制Task<TDerived>转换为Task<TBase>.

但是,您可以这样做:

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

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

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

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

4

3 回答 3

42

这样的方法存在吗?

不。

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

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

请注意,更通用的方法是扩展方法,Task例如Then. Stephen Toub在一篇博文中对此进行了探讨,我最近将其合并到 AsyncEx中。

使用Then,您的代码将如下所示:

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

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

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;
}
于 2013-03-20T17:07:44.720 回答
20

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

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

“最轻量”的替代方案是使用 aTaskCompletionSource<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;
}
于 2013-03-20T17:14:33.797 回答
1

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

于 2015-08-04T12:19:04.000 回答