2009-03-11 31 views
5

Recientemente tuve problemas al intentar AddRange (IEnumerable) en una lista. Probablemente sea un problema clásico, pero realmente no lo entiendo todavía.C# No se puede convertir de IEnumerable <Base> a IEnumerable <Derived>

Entiendo que los métodos que esperan un parámetro de lista no están satisfechos con una lista, ya que pueden intentar agregar una base a la lista, lo cual es obviamente imposible.

Pero si obtengo esto correctamente, dado que los IEnumerables no se pueden cambiar, debería funcionar en este caso.

El código pensé en el siguiente aspecto:

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 
    } 
} 

me trató de pasar una sugerencia para el compilador en el método AddRange2, pero esto sólo se trasladaron a un problema alrededor.

¿Es mi forma de pensar defectuosa? ¿Es esto una limitación del lenguaje o es por diseño?

IIRC, el soporte para este tipo de operaciones se agregó a Java 1.5, por lo que tal vez se agregará a C# en algún momento en el futuro, también ...?

+0

Duplicado de varias otras preguntas, incluyendo la más reciente http://stackoverflow.com/questions/632399 –

+0

De hecho, lo siento. Mi google-jutsu aún necesita mejoras. – mafu

+0

No hay problema. Sería bueno tener preguntas frecuentes por etiqueta, IMO ... –

Respuesta

18

Esto es covarianza, y se solucionará en C# 4.0/.NET 4.0. Por ahora, la opción genérica es la mejor respuesta (para IEnumerable<T> - not IList<T> etc).

Pero dentro del método genérico, tiene que pensar en términos de T. También puede usar Cast<T> o OfType<T> con LINQ para lograr algo similar.

+1

Gracias por el nuevo término, covarianza. No sabía sobre la existencia. – Sung

0

hay solución con el método de extensión:

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>()); 

pero el "< persona, Empleado>" es poco incómoda: /.

+0

Cast () sería más simple ... –

2

En C# 3.0 puede usar el método de extensión "Cast". Si importa System.Linq y luego usa este código:

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

Entonces debería funcionar para usted.

0

La solución de conversión, por supuesto, podría generar excepciones de lanzamiento de clase. La persona que publicó el trabajo de extensión enumerable dijo que era incómodo. me ocurrió una solución que es sólo la mitad de torpe, no sé si lo usaré:

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); 
    } 
} 

Uso:

mamíferos IEnumerable = UpTo < de Mamíferos > .Desde (perrera. Perros)

Cuestiones relacionadas