Creare un'attività completata <T>


125

Sto implementando un metodo Task<Result> StartSomeTask()e mi capita di conoscere il risultato già prima che venga chiamato il metodo. Come posso creare un'attività <T> che è già stata completata?

Questo è quello che sto facendo attualmente:

private readonly Result theResult = new Result();

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

C'è una soluzione migliore?


6
Nota, le risposte a questa domanda funzionano bene anche per la creazione di un'attività semplice (no <T>) perché l'attività <T> eredita dall'attività.
Tim Lovell-Smith,

Nota che oggi c'è ValueTaskper attività completate (cioè per valori che hai già in modo che il codice sia essenzialmente sincrono), il che ti farà risparmiare un'allocazione.
nawfal,

Risposte:


111
private readonly Result theResult = new Result();

public override Task<Result> StartSomeTask()
{
    var taskSource = new TaskCompletionSource<Result>();
    taskSource.SetResult(theResult);
    return taskSource.Task;
}

@DanielLobo potresti ottenere una risposta se spieghi qual è la tua obiezione
user2023861

1
Non dovrebbe essere quello qui sotto che è più semplice e con molti più voti positivi? @utente2023861
Daniel Lobo

203

Quando si sceglie come target .NET 4.5 è possibile utilizzare Task.FromResult:

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

Per creare un'attività non riuscita, utilizzare Task.FromException:

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

.NET 4.6 aggiunge Task.CompletedTaskse è necessario un non generico Task.

public static Task CompletedTask { get; }

Soluzioni alternative per le versioni precedenti di .NET:

  • Quando si sceglie come target .NET 4.0 con Async Targetting Pack (o AsyncCTP) è possibile utilizzare TaskEx.FromResultinvece.

  • Per ottenere non generici Taskprima di .NET 4.6, è possibile utilizzare il fatto che Task<T>deriva da Taske semplicemente chiamare Task.FromResult<object>(null)o Task.FromResult(0).


13
Per restituire un'attività non generica, è meglio usare qualcosa come Task.FromResult (0). L'uso di "null" come parametro può confondere il compilatore che non è in grado di determinare il parametro generico.
Whyllee,

Che dire delle eccezioni? I metodi asincroni vengono compilati nella macchina a stati che rileva le eccezioni e le salva nell'attività restituita. Questo succede anche per l'esecuzione del codice prima di attendere. Il metodo che restituisce Task.FromResult potrebbe generare eccezioni direttamente.
Robert Važan,

@ RobertVažan Un caso interessante. Probabilmente, se si sta recuperando il risultato noto da un metodo e tale metodo genera eccezioni, è necessario correggere un difetto.
Gusdor,

1
@ RobertVažan Puoi facilmente scrivere il tuo FromExceptionmetodo, che si comporta come FromResultma invece rappresenta un'attività difettosa. Un tale metodo può semplicemente restituirlo per i suoi casi di errore se è importante che l'eccezione sia rappresentata nell'attività risultante.
Servito il

1
Task.FromException non è disponibile in .NET 4.5 ... Penso che dovrebbe essere specificato.
STILE

12

Per le attività senza valore di ritorno, .NET 4.6 ha aggiunto Task.CompletedTask .

Restituisce un'attività che è già in TaskStatus.RanToCompletion. Probabilmente restituisce sempre la stessa istanza, ma la documentazione ti avverte di non contare su questo fatto.



1

Chiamare Task.WhenAll senza alcun parametro restituirà un'attività completata.

Task task = Task.WhenAll();

mentre questo funzionerà, è una soluzione oscura che potrebbe confondere le persone quando leggono il codice in quanto implica l'attesa di attività che non esistono
Adrian Hristov
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.