2009-03-11 37 views
5

Recentemente ho avuto problemi durante il tentativo di aggiungereRange (IEnumerable) a un elenco. Probabilmente un problema classico, ma non lo capisco davvero.C# Impossibile convertire da IEnumerable <Base> a IEnumerable <Derived>

Capisco che i metodi che si aspettano un parametro List non siano soddisfatti da un elenco, perché potrebbero provare ad aggiungere una Base all'elenco, il che è ovviamente impossibile.

Ma se ottengo questo correttamente, dal momento che IEnumerables non può essere modificato, dovrebbe funzionare in questo caso.

il codice che ho pensato di simile a questo:

class Foo 
{ 
} 

class Bar : Foo 
{ 
} 

class FooCol 
{ 
    private List<Foo> m_Foos = new List<Foo>(); 

    public void AddRange1(IEnumerable<Foo> foos) 
    { 
     m_Foos.AddRange (foos); // does work 
    } 

    public void AddRange2<T>(IEnumerable<T> foos) where T : Foo 
    { 
     m_Foos.AddRange (foos); // does not work 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     FooCol fooCol = new FooCol(); 

     List<Foo> foos = new List<Foo>(); 
     List<Bar> bars = new List<Bar>(); 

     fooCol.AddRange1 (foos); // does work 
     fooCol.AddRange1 (bars); // does not work 

     fooCol.AddRange2 (foos); // does work 
     fooCol.AddRange2 (bars); // does work 
    } 
} 

ho cercato di passare un suggerimento per il compilatore nel metodo AddRange2, ma questo appena trasferita a problema in giro.

Il mio modo di pensare è imperfetto? Si tratta di una limitazione della lingua o è di progettazione?

IIRC, il supporto per questo tipo di operazioni è stato aggiunto a Java 1.5, quindi forse sarà aggiunto a C# in futuro, anche in futuro ...?

+0

Duplicazione di varie altre domande, incluso più recentemente http://stackoverflow.com/questions/632399 –

+0

In effetti, mi dispiace. Il mio google-jutsu ha ancora bisogno di miglioramenti. – mafu

+0

Nessun problema. Sarebbe bello avere una FAQ per-tag, IMO ... –

risposta

18

Questa è covarianza e verrà risolta in C# 4.0/.NET 4.0. Per ora, l'opzione generica è la migliore risposta (per IEnumerable<T> - not IList<T> etc).

Ma all'interno del metodo generico, è necessario pensare in termini di T. Puoi anche utilizzare Cast<T> o OfType<T> con LINQ per ottenere qualcosa di simile.

+1

Grazie per il nuovo termine, covarianza. Non sapevo dell'esistenza. – Sung

0

C'è soluzione con metodo di estensione:

public static IEnumerable<TBase> ToBaseEnumerable<TBase, TDerived>(this IEnumerable<TDerived> items) where TDerived : TBase { 
    foreach(var item in items) { 
     yield return item; 
    } 
} 
... 
IEnumerable<Employee> employees = GetEmployees(); //Emplyoee derives from Person 
DoSomethingWithPersons(employees.ToBaseEnumerable<Person, Employee>()); 

ma il "< persona, dipendente>" è po 'scomoda: /.

+0

Cast () sarebbe più semplice ... –

2

In C# 3.0 è possibile utilizzare il metodo di estensione "Cast". Se si importa System.Linq e quindi si utilizza questo codice:

public void AddRange2<T>(IEnumerable<T> foos) where T : Foo 
{ 
    m_Foos.AddRange (foos.Cast<Foo>()); 
} 

Quindi dovrebbe funzionare per voi.

0

La soluzione di trasmissione, naturalmente, potrebbe generare eccezioni del cast di classe. La persona che ha pubblicato l'estensione enumerabile funziona dicendo che era imbarazzante. mi si avvicinò con una soluzione che è solo la metà scomoda, non so se lo userò:

public static class UpTo<T> 
{ 
    public static IEnumerable<T> From<F>(IEnumerable<F> source) where F:T 
    { 
     // this cast is guaranteed to work 
     return source.Select(f => (T) f); 
    } 
} 

Usage:

mammiferi IEnumerable = UpTo <Mammifero> .Da (canile. Cani)

Problemi correlati