Подтвердить что ты не робот

Как преобразовать задачу <TDerived> в задачу <TBase>?

Так как С# Task является классом, вы, очевидно, не можете использовать Task<TDerived> для Task<TBase>.

Однако вы можете сделать:

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

Есть ли статический метод задачи, который я могу вызвать, чтобы получить экземпляр Task<TDerived>, который по существу просто указывает на основную задачу и отображает результат? Мне хотелось бы что-то вроде:

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

Существует ли такой метод? Есть ли накладные расходы для использования асинхронного метода исключительно для этой цели?

4b9b3361

Ответ 1

Существует ли такой метод?

Нет.

Есть ли какие-либо издержки для использования метода async исключительно для этой цели?

Да. Но это самое простое решение.

Обратите внимание, что более общий подход - это метод расширения для Task, например Then. Стивен Туб исследовал эту в сообщении в блоге, а я недавно включил ее в 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;
}

Ответ 2

Существует ли такой метод? Есть ли накладные расходы для использования асинхронного метода исключительно для этой цели?

Для этого нет встроенного метода, и это вызывает накладные расходы.

Альтернативой "самый легкий вес" будет использование 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;
}

Ответ 3

вы можете попробовать следующее: task.ContinueWith<TDerived>( t => (TDerived)t.Result);