2010-03-22 32 views
5

Supponiamo, ad esempio, ho una classe:Perché una funzione che accetta IEnumerable <interface> non accetta IEnumerable <class>?

public class MyFoo : IMyBar 
{ 
    ... 
} 

Poi, vorrei utilizzare il seguente codice:

List<MyFoo> classList = new List<MyFoo>(); 
classList.Add(new MyFoo(1)); 
classList.Add(new MyFoo(2)); 
classList.Add(new MyFoo(3)); 

List<IMyBar> interfaceList = new List<IMyBar>(classList); 

Ma questo produce l'errore:

`Argument '1': cannot convert from 'IEnumerable<MyFoo>' to 'IEnumerable<IMyBar>' 

Perchè è questo? Poiché MyFoo implementa IMyBar, ci si aspetterebbe che un IEnumerable di MyFoo possa essere trattato come un IEnumerable di IMyBar. Un banale esempio del mondo reale che sta producendo una lista di macchine, e poi viene detto che non era un elenco di veicoli.

E 'solo un disturbo secondario, ma se qualcuno può far luce su questo, mi sarebbe molto obbligata.

+1

La risposta che cercate è stato anche coperto in questa domanda: http://stackoverflow.com/questions/2346763/any-simple-way-to- spiegare-perché-io-non-posso-listanimal-animals-new-arraylistdo/2346857 # 2346857 –

+0

Sì grazie - L'ho visto nella lista "correlata" una volta che ho postato la domanda, ma non è venuto fuori mentre Stavo scrivendolo ... –

risposta

14

questo sta andando a lavorare in C# 4.0! Quello di cui parli si chiama covarianza generica.

Nel frattempo è possibile utilizzare il Cast extension method:

List<IMyBar> interfaceList = new List<IMyBar>(classList.Cast<IMyBar>()); 
+1

Ok, brillante, ma in realtà non sembra una scienza missilistica - c'è qualche motivo particolare per cui non è stato supportato in precedenza? –

+0

@Matt: Non è una scienza missilistica, ma come qualsiasi altra funzione, ci vuole tempo e denaro per implementare e ci sono state alcune grandi caratteristiche con priorità più alta che hanno implementato prima (ad esempio LINQ è davvero buono, no?). Dovrai tagliare alcune grandi caratteristiche per spedire un prodotto in tempo. - Una cosa da notare su questa caratteristica specifica è che richiede il supporto dal sottostante CLR e CLR 2.0 non lo supporta. Il supporto è stato aggiunto in CLR 4.0. –

+0

Grazie - molto utile ... :) –

3

Questo è supportato in .NET 4.0, ma non prima.

http://msdn.microsoft.com/en-us/library/dd799517%28VS.100%29.aspx

+1

Tecnicamente è stato supportato in Net Via Il dato 2,0, ma ha guadagnato solo il supporto a C# e VB.Net in 4,0 – JaredPar

+0

@ JaredPar: Davvero? Il CLR 2.0 supporta la covarianza generica in * verifiable * IL? –

+0

@ Mehrdad, Sì, credo di si. Sono passati più di 1 anno da quando ho avuto questa discussione con Lucian ma il mio attuale ricordo è che CLR supportava la co/contro-varianza dal 2.0 e che 4.0 era tutto nei compilatori (forse una correzione di bug o due nel CLR). – JaredPar

3

Per chiarire, il motivo per cui non funziona la società è perché IEnumerable<MyClass> non eredita da IEnumerable<MyBar>. Lo dirò di nuovo: l'enumerabile del tipo derivato non eredita dall'enumerabile del tipo base. Piuttosto, entrambi i tipi sono specializzati nel tipo generico IEnumerable<T>. In .Net 3.5 e versioni precedenti, non hanno altre relazioni oltre alla specializzazione (che non è ereditarietà) e sono altrimenti due tipi completamente diversi nel sistema dei tipi.

.Net 4.0 non cambierà il modo in cui questi tipi si riferiscono a tutti. Saranno comunque due tipi completamente diversi che riguardano solo la specializzazione. Quello che farà è consentire di scrivere metodi che conoscono la relazione di specializzazione e in alcuni casi consentire di sostituirne uno quando si scrive l'altro.

+1

Per chiarire il secondo paragrafo: il punto della varianza generica è che altera la relazione "è assegnata compatibile con" su alcuni tipi generici. Questa è la relazione rilevante; la relazione ereditaria è invariata, come dici tu. –

+0

Joel, non ho mai pensato a co-varianza/contr varianza in questo modo. Risposta molto utile, +1 da parte mia. – SolutionYogi

1

Se vuoi lanciare tra IEnumerables (come hai scritto nel titolo della tua domanda) e non tra le liste (come hai scritto nella tua domanda) si può attendere che la funzione di covarianza C# 4.0. Prima di quel giorno è possibile utilizzare i metodi di estensione. Ma non vorrei usare lo Cast extension method annotato in altre risposte, ma scrivendo il mio metodo, che può essere usato solo per la fusione di covarianza in IEnumerable. Quando/Se passi a C# 4.0 troverai facilmente tutti i posti nel tuo codice in cui il cast sarà ridondante.

public static class cEnumerableExtensions 
{ 
    public static IEnumerable<TResult> CovarianceConversion<TResult, TSource>(this IEnumerable<TSource> source) 
     where TSource : TResult 
    { 
     foreach (var item in source) 
     { 
      yield return item; 
     } 
    } 
} 

Nodo finale. Sembra che ci sia no precompiler constant for the netframework 4.0, altrimenti avrei aggiungere

#if DOTNET4 
    [Obsolete("You can directly cast in C# 4.0.")] 
    #endif 
Problemi correlati