Utwórz ukończone zadanie <T>

125

Implementuję metodę Task<Result> StartSomeTask()i tak się składa, że ​​znam wynik jeszcze przed wywołaniem metody. Jak utworzyć zadanie <T>, które zostało już zakończone?

Oto, co obecnie robię:

private readonly Result theResult = new Result();

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

Czy jest lepsze rozwiązanie?

dtb
źródło
6
Zwróć uwagę, że odpowiedzi na to pytanie działają również dobrze w przypadku tworzenia zwykłego zadania (bez <T>), ponieważ Task <T> dziedziczy z Task.
Tim Lovell-Smith
Zauważ, że dzisiaj są dostępne ValueTaskdla ukończonych zadań (tj. Dla wartości, które już masz, więc kod jest zasadniczo synchroniczny), co pozwoli Ci zaoszczędzić przydział.
nawfal

Odpowiedzi:

111
private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var taskSource = new TaskCompletionSource<Result>();
    taskSource.SetResult(theResult);
    return taskSource.Task;
}
QrystaL
źródło
@DanielLobo możesz otrzymać odpowiedź, jeśli wyjaśnisz swój sprzeciw
user2023861
1
Czy nie powinien to być ten poniżej, który jest prostszy i ma dużo więcej głosów pozytywnych? @ user2023861
Daniel Lobo
203

W przypadku platformy .NET 4.5 można użyć Task.FromResult:

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

Aby utworzyć zadanie, które się nie powiodło, użyj Task.FromException:

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

NET 4.6 dodaje, Task.CompletedTaskjeśli potrzebujesz niestandardowego Task.

public static Task CompletedTask { get; }

Obejścia dla starszych wersji platformy .NET:

  • W przypadku platformy .NET 4.0 z pakietem Async Targetting Pack (lub AsyncCTP) można użyć TaskEx.FromResultzamiast tego.

  • Aby uzyskać nieogólną wersję Taskprzed .NET 4.6, możesz użyć faktu, który Task<T>pochodzi z Taski po prostu wywołać Task.FromResult<object>(null)lub Task.FromResult(0).

CodesInChaos
źródło
13
Aby zwrócić nieogólne zadanie, lepiej użyć czegoś takiego jak Task.FromResult (0). Użycie „null” jako parametru może zmylić kompilator, który nie może określić parametru ogólnego.
Whyllee
A co z wyjątkami? Metody asynchroniczne są kompilowane do maszyny stanowej, która przechwytuje wyjątki i zapisuje je w zwróconym Task. Dzieje się tak nawet w przypadku wykonywania kodu przed pierwszym oczekiwaniem. Metoda zwracająca Task.FromResult może bezpośrednio zgłaszać wyjątki.
Robert Važan
@ RobertVažan Ciekawy przypadek krawędzi. Prawdopodobnie, jeśli pobierasz swój znany wynik z metody i ta metoda rzuca wyjątki, oznacza to, że istnieje defekt, który wymaga naprawy.
Gusdor
1
@ RobertVažan Możesz łatwo napisać własną FromExceptionmetodę, która zachowuje się jak FromResultzadanie, ale zamiast tego przedstawia błędne zadanie. Taka metoda może po prostu zwrócić to dla swoich przypadków błędów, jeśli ważne jest, aby wyjątek był reprezentowany w wynikowym zadaniu.
Servy
1
Task.FromException nie jest dostępny w .NET 4.5 ... Myślę, że powinien być określony.
STiLeTT
12

W przypadku zadań bez wartości zwracanej .NET 4.6 dodał Task.CompletedTask .

Zwraca zadanie, które jest już w TaskStatus.RanToCompletion. Prawdopodobnie za każdym razem zwraca tę samą instancję, ale dokumentacja ostrzega, aby nie liczyć na ten fakt.

Daryl
źródło
1

Jeśli używasz Rx, alternatywą jest Observable.Return (result) .ToTask ().

Niall Connaughton
źródło
1

Wywołanie Task.WhenAll bez parametrów zwróci ukończone zadanie.

Task task = Task.WhenAll();
zumalifeguard
źródło
chociaż to zadziała, jest to niejasne obejście, które może zmylić ludzi podczas czytania kodu, ponieważ oznacza to oczekiwanie na zadania, które nie istnieją
Adrian Hristov