2012-02-20 15 views
13

UPDATE: Il seguente codice ha senso solo in C# 4.0 (Visual Studio 2010)covarianza/controvarianza: come rendere il seguente codice compilare

Sembra che io sto avendo qualche incomprensione della covarianza/cosa controvarianza . Qualcuno può dirmi perché il seguente codice non viene compilato?

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
     where TDerived: TBase 
    { 
     return values; 
    } 
} 

mentre questo compila: (!!!)

public interface IBase 
{ 
} 
public interface IDerived: IBase 
{ 
} 
public class TestTwo 
{ 
    public IEnumerable<IBase> Method(IEnumerable<IDerived> values) 
    { 
     return values; 
    } 
} 

risposta

13

covarianza si applica solo per fare riferimento a tipi (per gli argomenti di tipo), quindi si deve aggiungere un vincolo di classe:

public IEnumerable<TBase> Method<TDerived>(IEnumerable<TDerived> values) 
    where TDerived : class, TBase 
{ 
    return values; 
} 

Ciò impedirà di tentare di convertire, ad esempio, uno IEnumerable<int> in un IEnumerable<object>, che non è valido.

+0

@ Adamo: Credo che vi sbagliate, '' IEnumerable non è '' IEnumerable di default in modo che non sarebbe compilare anche in '3.5' – sll

+1

@AdamMihalcin: No, che il codice * no * ho compilato prima di .NET 4. Ho appena provato da solo per verificarlo. Senza invarianza generica, la conversione da 'IEnumerable ' a 'IEnumerable ' è semplicemente non valida. –

+0

buona chiamata su TSuper, appena corretto che –

1

Non riesco a pensare a nessuna situazione in cui sia effettivamente necessario TDerived. Utilizzando TBase è sufficiente:

public class TestOne<TBase> 
{ 
    public IEnumerable<TBase> Method(IEnumerable<TBase> values) 
    { 
     return values; 
    } 
} 

Dopo tutto, si ha nessuna informazione su TDerived a parte il fatto che si tratta di un TBase ...

+2

sfortunatamente non è qualcosa che ho inventato per motivi di complessità, è una situazione reale che ho finito per avere nel mio codice –

+1

@bonomo: è interessante, mi piacerebbe vedere un esempio più dettagliato per riferimento futuro. :-) – linepogl

0

Né compilato per me inizialmente. Entrambi hanno fallito il cast implicito da Super (T/I) a Base (T/I). Quando ho aggiunto un caso esplicito, tuttavia, entrambi sono stati compilati.

public IEnumerable<TBase> Method<TSuper>(IEnumerable<TSuper> values) 
    where TSuper: TBase 
    { 
     return (IEnumerable<TBase>) values; 
    } 
Problemi correlati