2016-07-01 28 views
6

Dato il frammento di followin, io piuttosto non capisco PERCHE quello che im intenzione di raggiungere non è possibile:covarianza e controvarianza su Compiti

Interfaccia:

public interface IEntityRepository<out T> : IRepository<IEntity> { 

    void RequeryDataBase(); 

    IEnumerable<T> Search(string pattern); 

    Task<IEnumerable<T>> SearchAsync(string pattern); 

    SearchContext Context { get; } 

    string BaseTableName { get; } 
    } 

In IRepository<IEntity> sono solo semplici generiche CRUD definito.

ottengo un errore su questa linea: Task<IEnumerable<T>> SearchAsync(string pattern);

Errore:

method return type must be output safe. invalid variance: the type parameter T must be invariantly valid on Task

Si prega di aiutarmi a capire, perché i cant uso <out T> con un Task<T>

+0

Penso che il risultato dell'operazione deve essere assegnato al compito, il che rende un '' Jim

+0

@Jim ma il tipo di risultato è 'iEnumerable ', non solo 'T' .... Inutile pensare che questo dovrebbe funzionare. –

+2

'Task ' è una classe quindi è invariabile. – Lee

risposta

9

Task<T> non è covariante. La varianza può essere applicata solo alle interfacce generiche (e delegate, ma non è rilevante qui).

ad es. Task<IEnumerable<Dog>>non può essere assegnato a Task<IEnumerable<Animal>>. Per questo motivo, la tua interfaccia non può essere contrassegnata come covariante.

Si potrebbe desiderare di vedere this related question.

+2

Possono dare tutte le spiegazioni che vogliono. Mi sento ancora come qualcuno ha fatto la chiamata sbagliata quando si introduce 'Task ', 'ITask' ma no' ITask '. – Toxantron

5

momento di decidere su varianza di un generico digita argomento in qualche interfaccia generica, devi tenere conto di tutti gli usi dell'argomento di tipo generico all'interno dell'interfaccia. Ogni utilizzo può introdurre alcuni vincoli relativi alla varianza. Questo include:

  • utilizza come argomento ingresso ad un metodo - non consente covarianza
  • utilizza come valore restituito da un metodo - non consente controvarianza
  • utilizza come parte di altri genetico deriva, come Task<T> - tale uso può non consentire la covarianza, non consentire la contravarianza o entrambi

Stavo usando la logica negativa per sottolineare che tutti questi casi stanno introducendo fondamentalmente dei vincoli. Dopo l'analisi, saprai se qualche elemento ha annullato la covarianza e quindi il tuo argomento potrebbe non essere dichiarato come out. Viceversa, se un elemento ha una controvarianza non consentita, il tuo argomento potrebbe non essere dichiarato come in.

Nel tuo caso particolare, il primo metodo Search restituisce IEnumerable<T>, rendendo controvarianza non applicabile. Ma, SearchAsync sta restituendo Task<IEnumerable<T>> e quell'utilizzo introduce vincoli che esistono nel tipo di attività - è invariante, il che significa che questa volta out è anche impossibile.

Il risultato è che l'interfaccia generica deve essere invariabile sul tipo di argomento generico per soddisfare le firme di tutti i suoi metodi.

+0

Non capisco completamente perché non ci sia l'interfaccia di ITask . Sarebbe di aiuto con quei problemi. – Toxantron

+0

@Toxantron perché qualcuno deve _set_ il risultato, quindi non può essere covariante. Quello che ancora non vedo è come tutto ciò si applica qui, poiché il tipo in questione è ancora 'IEnumerable ' e non 'T'. Perché la varianza di 'T' gioca un ruolo in' Task > '? –

+1

@ RenéVogt 'Task' è invariante su' IEnumerable ', che in transito significa che T non deve variare, nonostante il fatto che' IEnumerable 'stesso sia covariante su' T'. Il metodo 'Search' restituisce plain' IEnumerable 'e questo non vincola la varianza. Con solo il metodo 'Search', l'intera interfaccia potrebbe essere covariante su' T'. –

0

Si può imbrogliare e utilizzare

IObservable<out T> 

che è quasi lo stesso Task<T> ma con un tipo covariante. Può sempre essere convertito in un compito quando ne hai bisogno. Si perde un po 'nella leggibilità del codice perché si presume (e si applica) con Task<T> che si ottiene solo un risultato ma con IObservable<T> è possibile ottenerne molti. Tuttavia si può fare

IObservable<T> o = ... 
var foo = await o; 

proprio come

Task<T> t = ... 
var foo = await t;