2009-10-15 27 views
5

Dati i seguenti:IEnumerable <T> conversione


class Base<T> {/*...*/} 
class Der<T>: Base<T> {/*...*/} 

interface Sth<T>{ 
    IEnumerable<Base<T>> Foo {get;} 
} 

// and implementation... 
class Impl<T>: Sth<T> { 
    public IEnumerable<Base<T>> Foo { 
    get { 
     return new List<Der<T>>(); 
    } 
    } 
} 

Come posso ottenere questo per compilare? L'errore è, ovviamente, non la conversione implicita trovata dalla lista < Der < T >> alla lista < Base < T >>. Se lo lancio espressamente, si verifica InvalidCastException.

+4

Se questo argomento vi interessa, si potrebbe desiderare di leggere la mia serie enorme sulla progettazione di questa funzionalità in C# 4.0: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+ Contravariance/default.aspx –

risposta

6

La conversione che si sta tentando di eseguire non è supportata dalla versione 3.5 di .NET Framework. Sarà supportato nella versione 4.0 (Visual Studio 2010) a causa dell'aggiunta di generic covariance/contravariance. Anche se questo non consentirà ancora di lanciare List<Der>-List<Base>, si vi permetterà di lanciare IEnumerator<Der> (che implementa la lista) per IEnumerator<Base>.

Nel frattempo, è possibile scrivere la propria classe che implementa IEnumerable<Base> e restituisce un costume IEnumerator<Base> che avvolge semplicemente List<Der> s' IEnumerator<Der>. In alternativa, se si utilizza .NET Framework versione 3.5, è possibile utilizzare Cast extension method, come suggerito da altri.

+1

Si noti che la varianza generica non consentirà di eseguire il cast di un elenco a un elenco , poiché l'elenco dispone di membri in entrata e in uscita di tipo T. (Funzionerà * per l'utilizzo di un oggetto IEnumerable in posizione di un oggetto IEnumerable .) – itowlson

+0

Vero, ma, se ho capito bene, consentirà la compilazione del codice di esempio dell'OP.'Lista ' implementa 'IEnumerable ', che può essere castato a 'IEnumerable '. – bcat

+0

Certo, credo di sì; L'ho solo menzionato perché nel testo della domanda menzionava le conversioni implicite tra i tipi di elenco, quindi volevo chiarire che il tuo commento si applicava al suo codice (che utilizza IEnumerable), non al suo commento supplementare (che si riferiva a List). – itowlson

1

Per rendere più compilare ...

class Impl<T> : Sth<T> 
{ 
    public IEnumerable<Base<T>> Foo 
    { 
     get 
     { 
      return new List<Base<T>>(); //change the List type to Base here 
     } 
    } 
} 

Si può sempre fare qualcosa di simile anche, che sarebbe tornato l'IEnumerable della classe Base da un'implementazione di Der

class Impl<T> : Sth<T> 
{ 
    public IEnumerable<Base<T>> Foo 
    { 
     get 
     { 
      List<Der<T>> x = new List<Der<T>>(); 

      foreach (Der<T> dt in x) 
      { 
       yield return dt; 
      } 
     } 
    } 
} 
4

List<Der<T>> è non convertibile in List<Base<T>> perché quest'ultimo può avere un Base<T> aggiunto e il primo non può.

È possibile risolvere questo utilizzando il metodo di estensione Cast: return new List<Der<T>>().Cast<Base<T>>();

1

È possibile utilizzare LINQ per lanciare da List<Der<T>> a un IEnumerable<Base<T>>, utilizzando:

class Impl<T>: Sth<T> 
{ 
    public IEnumerable<Base<T>> Foo 
    { 
     get 
     { 
      return new List<Der<T>>().Cast<Base<T>>(); 
     } 
    } 
} 

Come le altre risposte hanno dichiarato, convariance generica è non supportato in v3.5, ma puoi usare LINQ per creare un oggetto wrapper che implementa IEnumerable<Base<T>>.

+0

Grazie, questo mi ha aiutato. Quanto il cast rallenterà l'applicazione? Ha un grande impatto sulle prestazioni? – SOReader

+0

Prendi un cronometro, provalo qualche milione di volte, e poi lo saprai. Sei l'unico che ha accesso ai dati di cronometraggio per la tua applicazione; non possiamo rispondere a questa domanda. –

+0

vacci piano, Eric =] Ho appena chiesto se ha mai controllato l'overhead ... – SOReader

0

Queste risposte mi hanno aiutato, e volevo solo postare il mio soluzione per un problema simile che ho avuto.

Ho scritto un metodo di estensione per IEnumerable che proietta automaticamente TSource ogni volta che voglio convertire un elenco <Foo> a un oggetto IEnumerable <Bar> (Sono ancora in 3.5).

public static SpecStatus Evaluate<TSource, TSpecSource>(this IEnumerable<TSource> source, ISpec<IEnumerable<TSpecSource>> spec) 
     where TSource : TSpecSource 
{ 
    return spec.Evaluate(source.Cast<TSpecSource>()); 
} 
Problemi correlati