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.