2015-06-23 15 views
34
class ResultBase {} 
class Result : ResultBase {} 

Task<ResultBase> GetResult() { 
    return Task.FromResult(new Result()); 
} 

Il compilatore mi informa che non è possibile convertire implicitamente Task<Result> in Task<ResultBase>. Qualcuno può spiegare perché questo è? Mi sarei aspettato una co-varianza per permettermi di scrivere il codice in questo modo.Perché l'attività <T> non è co-variante?

+2

Le interfacce possono essere solo covarianti o controvarianti. Le classi sono sempre invarianti. Ulteriori informazioni su: http://stackoverflow.com/questions/13107071/why-classes-that-implement-variant-interfaces-remain-invariant –

+0

Le classi sono invarianti in C#. – Lee

+3

Da [questa risposta] (http://stackoverflow.com/questions/12204755/can-should-tasktresult-be-wrapped-in-ac-sharp-5-0-awaitable-which-is-covarian) sembra che qualcuno ha scritto [un wrapper criptato ITask ] (https://github.com/jam40jeff/ITask) per questo. Inoltre si può votare [un suggerimento per implementarlo qui] (https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/5754247-make-task-t-implement-covariant-interface-itask- o). –

risposta

17

Secondo someone who may be in the know ...

La giustificazione è che il vantaggio di covarianza viene superato dal lo svantaggio di disordine (cioè tutti avrebbero dovuto prendere una decisione su se utilizzare Task o ITask in ogni singolo posto nel loro codice).

Mi sembra che in entrambi i casi non vi sia una motivazione molto convincente. ITask<out T> richiederebbe un sacco di nuovi sovraccarichi, probabilmente un bel po 'sotto il cofano (non posso attestare come viene implementata la classe base effettiva o quanto sia speciale rispetto a un'implementazione ingenua) ma molto più nella forma di questi linq -like metodi di estensione.

Qualcun altro ha fatto un buon punto: il tempo sarebbe stato meglio speso rendendo class es covariante e controverso. Non so quanto sarebbe difficile, ma mi sembra un uso migliore del tempo.

D'altra parte, qualcuno ha detto che sarebbe molto bello avere una vera funzionalità simile a yield return disponibile in un metodo async. Voglio dire, senza giochi di prestigio.

+5

'async',' await' si basa sull'esistenza di un metodo 'GetAwaiter' adatto quindi è già disaccoppiato dalla classe' Task'. – Lee

+0

Sono in piedi modificato/corretto –

6

mi accorgo di essere in ritardo alla festa, ma qui è un metodo di estensione che ho usato per tenere conto di questa mancanza:

/// <summary> 
/// Casts the result type of the input task as if it were covariant 
/// </summary> 
/// <typeparam name="T">The original result type of the task</typeparam> 
/// <typeparam name="TResult">The covariant type to return</typeparam> 
/// <param name="task">The target task to cast</param> 
[MethodImpl(MethodImplOptions.AggressiveInlining)] 
public static Task<TResult> AsTask<T, TResult>(this Task<T> task) 
    where T : TResult 
    where TResult : class 
{ 
    return task.ContinueWith(t => t.Result as TResult); 
} 

questo modo si può fare solo:

class ResultBase {} 
class Result : ResultBase {} 

Task<ResultBase> GetResult() 
{ 
    return Task.FromResult(new Result()).AsTask<Result, ResultBase>(); 
} 
Problemi correlati