2012-08-30 7 views
11

Mi piace molto lavorare con la programmazione asincrona C# 5.0. Tuttavia, ci sono alcuni punti in cui l'aggiornamento del vecchio codice per essere coerente con il modello TAP sta causando problemi.È possibile/deve eseguire l'attività <TResult> in un C# 5.0 attendibile che è covariante in TResult?

Ecco uno di loro - io non so esattamente il motivo per cui Task<TResult> non è covariante nel TResult, ma è causando problemi per me, quando si cerca di aggiornare un'interfaccia covariante di passare da un sincrono a un modello asincrono:

vecchio codice:

public interface IInitializable<out T> // ** out generic modifier ** 
{ 
    /// <summary> 
    /// Boolean to indicate if class is ready 
    /// </summary> 
    bool IsInitialized { get; } 

    /// <summary> 
    /// Calls for instance to be initialized using current parameters 
    /// Driver initialization can be done in the default constructor if desired 
    /// </summary> 
    T Initialize(); 
} 

Nuovo codice (non verrà compilato):

public interface IAsyncInitializable<out T> // ** out generic modifier...broken ** 
{ 
    /// <summary> 
    /// Boolean to indicate if class is ready 
    /// </summary> 
    bool IsInitialized { get; } 

    /// <summary> 
    /// Calls for instance to be initialized using current parameters 
    /// Driver initialization can be done in the default constructor if desired 
    /// </summary> 
    Task<T> InitializeAsync(); // ** breaks because Task<TResult> is invariant in TResult ** 
} 

è che c'è un modo ragionevole intorno a questo senza mo difendendo le mie API troppo drasticamente? (Bonus: perché Task non è covariante?). Non esiste un'interfaccia IAwaitable, ma suppongo che potrei crearne uno e creare un metodo di estensione che converta in un oggetto task avvolto, covariante e avvincente. O sto sbagliando?

+2

BTW, anche se 'Task' era un'interfaccia covariante, il codice sarebbe essere compilato. La versione corretta sarebbe 'Task InitializeAsync();' (senza il modificatore 'out' su quella linea). – svick

+0

Un punto eccellente. L'ho corretto in VS, ma ho dimenticato di modificare SO. –

risposta

12

non può essere covariante in T, perché è una classe. Solo le interfacce e i delegati possono avere varianza generica.

Se vale la pena fare il wrapping ... Immagino che dipenda da quanto usi la covarianza all'interno del tuo progetto. Ho il sospetto che tu possa trovare tutto il wrapping e lo scartare confuso nel tempo, per essere onesti - se non è lo troppo male prendere solo il colpo di rimuovere la covarianza, lo farei.

+0

Grazie per il consiglio! Ho implementato IAwaitable (out TResult) usando il tuo TaskAwaiter (T) Eduasync, e posso vedere che l'uso di un'interfaccia separata non vale sicuramente la perdita di leggibilità/usabilità. E, ottimo punto: classi e varianza generica ... onorato di aver segnalato il mio errore @Jon Skeet :). Nessun ITask (fuori T) è un fiasco, però. Vedo il commento di Stephen Cleary qui (http://stackoverflow.com/questions/8407227/async-generic-delegate-in-c-sharp-5-0), ma la compatibilità di interoperabilità di WinRT è un po 'insoddisfacente come giustificazione per l'utilizzo il tipo concreto. –

+1

@DavidCuccia: Dovresti leggere il blog di Stephen Toub sulla propagazione dei contesti quando si tratta di attendenti. ( –

+0

@JonSkeet - Qualche idea sul perché gli sviluppatori di framework non aggiungessero un 'ITask ' e implementasse solo una classe? –

4

Credo che non includere il supporto del compilatore per la parola chiave async su un'interfaccia ITask è stata una grande svista da parte di Microsoft. Fortunatamente non è troppo difficile aggirare questa limitazione.

Ho implementato un'interfaccia covariante ITask<out TResult>. Il suo utilizzo è piuttosto semplice.

Maggiori informazioni sono disponibili all'indirizzo:

https://github.com/jam40jeff/ITask

Problemi correlati