Questo codice dovrebbe restituire un'attività o un'attività ?
-
21-12-2019 - |
Domanda
Stavo leggendo La natura del taskcompletitionsource , un post di Stephen Toub.
public static Task RunAsync(Action action)
{
var tcs = new TaskCompletionSource<Object>();
ThreadPool.QueueUserWorkItem(_ =>
{
try
{
action();
tcs.SetResult(null);
}
catch(Exception exc) { tcs.SetException(exc); }
});
return tcs.Task;
}
.
.
.Dal momento che non ci interessa più quale sia il tipo di
T
, sono predefinito per utilizzareObject
. Quindi, quando ilAction
viene eseguito con successo,SetResult
è ancora utilizzato per passare alTask
nello stato finale diRanToCompletion
; Tuttavia, dal momento che il valore del risultato effettivo è irrilevante, viene utilizzatonull
. Infine,RunAsync
restituisceTask
anzichéTask<Object>
. Naturalmente, il tipo istanziato deltask
è ancoraTask<Object>
, ma non abbiamo bisogno di riferirci come tali, e il consumatore di questo metodo non è necessario cure di tali dettagli di implementazione.
Non capisco particolarmente perché il metodo dovrebbe restituire Task
anziché Task<object>
(ed è per questo che ho sottolineato la sentenza audace). So che il metodo è impostato per ritornare Task
ma tcs
è un TaskCompletionSource<Object>
, non TaskCompletionSource
(che è sbagliato, penso).
Soluzione
Non c'è un TaskCompletionSource
non generico e considerando tutto ciò che desideri è un'attività senza risultato, il risultato non ha importanza.
Il chiamante non lo sa e non importa in questo caso che l'attività sia in realtà un Task<object>
, il chiamante è solo generacodicitagcode, e ottiene un'eccezione se ce n'è uno.Il chiamante non è consapevole del risultato effettivo.
Questo ovviamente è facilitato dal fatto che await
eredita da Task<T>
.
È anche comune trovare un Task
che restituisce false o Task<bool>
con 0.
Altri suggerimenti
Non esiste una classe TaskCompletionSource
non generica per la creazione di istanze di Task
che non sono istanze di Task<T>
. Questo lascia due opzioni per il parametro tipo generico per TaskCompletionSource<T>
quando non ti interessa (o non forniscono) il valore di ritorno:
- .
- Utilizzare un tipo esistente arbitrario, come
object
, come tipo di ritorno. Impostare il valore sunull
per indicare il completamento dell'attività. - Utilizzare uno specifico tipo non pubblico e impostare il valore su
null
per indicare il completamento dell'attività.
Quando creo un'istanza TaskCompletionSource<T>
allo scopo di fornire un Task
senza valore di ritorno, preferisco utilizzare un tipo non pubblico dedicato per garantire che il codice di consumo non scambiasse il Task
restituito come un'istanza di Task<T>
in cui il risultato ha significato.
In primo luogo, definisco la seguente classe (può essere un private sealed class
se è annidato all'interno di un altro tipo):
internal sealed class VoidResult
{
}
.
Quindi, invece di utilizzare TaskCompletionSource<object>
per la sorgente di completamento, utilizzo TaskCompletionSource<VoidResult>
. Poiché il tipo VoidResult
non è accessibile con il codice di chiamata, l'utente non sarà in grado di lanciare l'oggetto Task
a un'istanza di Task<VoidResult>
.
.Non capisco particolarmente perché il metodo dovrebbe restituire
Task
anzichéTask<object>
Poiché quando si ritorna Task<Object>
significa che quando questo metodo lo completa produrrà un utile valore di tipo Object
.In questo caso non stiamo producendo alcun risultato, ecco perché Stephen sceglie di tornare Task
.
Se abbiamo a che fare con Func<Object>
, il restituzione di Task<Object>
sarebbe appropriato, poiché Func
produrrà qualche risultato, possiamo scegliere di restituirlo.
.Perché
TaskCompletionSource<Object>
, nonTaskCompletionSource
?
Perché non c'è niente del genere.Non esiste un TaskCompletionSource
non generico.
Se è stato restituito un Task<object>
, allora var result = await RunAsync(...)
restituirebbe sempre null
, dal momento che è quello che stai impostando il risultato in.
Il cliente non si preoccupa di questo, quindi restituisci un Task
.
Idealmente, useresti un TaskCompletionSource
internamente, invece di un TaskCompletionSource<object>
, e semplicemente chiamare qualcosa come SetCompleted()
anziché SetResult(null)
.Ma tale tipo non esiste.