Создать завершенную задачу <T>

125

Я реализую метод Task<Result> StartSomeTask()и знаю результат еще до вызова метода. Как мне создать Task <T> , который уже выполнен?

Вот чем я сейчас занимаюсь:

private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var task = new Task<Result>(() => theResult);
    task.RunSynchronously(CurrentThreadTaskScheduler.CurrentThread);
    return task;
}

Есть ли лучшее решение?

DTB
источник
6
Обратите внимание, что ответы на этот вопрос также отлично подходят для создания простой задачи (без <T>), потому что Task <T> наследуется от Task.
Тим Ловелл-Смит,
Обратите внимание, что сегодня есть ValueTaskдля завершенных задач (т.е. для значений, которые у вас уже есть, так что код по существу синхронный), что сэкономит вам выделение.
nawfal

Ответы:

111
private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var taskSource = new TaskCompletionSource<Result>();
    taskSource.SetResult(theResult);
    return taskSource.Task;
}
QrystaL
источник
@DanielLobo, вы можете получить ответ, если объясните, в чем
заключаются
1
Разве это не должен быть тот, который ниже, который проще и с гораздо большим количеством голосов? @ user2023861
Даниэль Лобо
203

При ориентации на .NET 4.5 вы можете использовать Task.FromResult:

public static Task<TResult> FromResult<TResult>(TResult result);

Чтобы создать неудавшуюся задачу, используйте Task.FromException:

public static Task FromException(Exception exception);
public static Task<TResult> FromException<TResult>(Exception exception);

.NET 4.6 добавляет, Task.CompletedTaskесли вам нужен не общий Task.

public static Task CompletedTask { get; }

Обходные пути для более старых версий .NET:

  • При ориентации на .NET 4.0 с помощью Async Targetting Pack (или AsyncCTP) вы можете использовать TaskEx.FromResultвместо этого.

  • Чтобы получить неуниверсальный пакет Taskдо .NET 4.6, вы можете использовать тот факт, который является Task<T>производным от Taskи просто вызвать Task.FromResult<object>(null)или Task.FromResult(0).

CodesInChaos
источник
13
Чтобы вернуть не общую задачу, лучше использовать что-то вроде Task.FromResult (0). Использование "null" в качестве параметра может запутать компилятор, который не может определить общий параметр.
Whyllee,
А как насчет исключений? Асинхронные методы компилируются в конечный автомат, который перехватывает исключения и сохраняет их в возвращенной задаче. Это происходит даже при выполнении кода до первого ожидания. Метод, возвращающий Task.FromResult, может напрямую вызывать исключения.
Роберт Важан
@ RobertVažan Интересный крайний случай. Возможно, если вы извлекаете свой известный результат из метода, и этот метод выдает исключения, значит, имеется дефект, который необходимо исправить.
Gusdor
1
@ RobertVažan Вы можете легко написать свой собственный FromExceptionметод, который ведет себя как, FromResultно вместо этого представляет собой некорректную задачу. Такой метод может просто возвращать это для своих случаев ошибок, если важно, чтобы исключение было представлено в результирующей задаче.
Servy
1
Task.FromException недоступен в .NET 4.5 ... Я думаю, это следует указать.
STiLeTT 01
12

Для задач без возвращаемого значения в .NET 4.6 добавлен Task.CompletedTask .

Он возвращает задачу, которая уже находится в TaskStatus.RanToCompletion. Вероятно, он каждый раз возвращает один и тот же экземпляр, но документация предупреждает, что не стоит рассчитывать на этот факт.

Дэрил
источник
1

Если вы используете Rx, альтернативой является Observable.Return (result) .ToTask ().

Найл Коннотон
источник
1

Вызов Task.WhenAll без каких-либо параметров вернет завершенную задачу.

Task task = Task.WhenAll();
zumalifeguard
источник
хотя это будет работать, это непонятный обходной путь, который может запутать людей при чтении кода, поскольку подразумевает ожидание несуществующих задач
Адриан Христов