Как преобразовать задачу <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());
}
Существует ли такой метод? Есть ли накладные расходы для использования асинхронного метода исключительно для этой цели?
Ответы
Ответ 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);