2010-03-22 27 views
5

Digamos, por ejemplo, tengo una clase:¿Por qué una función que toma IEnumerable <interface> no acepta IEnumerable <class>?

public class MyFoo : IMyBar 
{ 
    ... 
} 

Entonces, me gustaría utilizar el siguiente código:

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

Pero esto produce el error:

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

¿Por qué es esto? Como MyFoo implementa IMyBar, uno esperaría que un IEnumerable de MyFoo se pudiera tratar como un IEnumerable de IMyBar. Un ejemplo mundano del mundo real es producir una lista de autos y luego decirles que no era una lista de vehículos.

Es solo una pequeña molestia, pero si alguien puede arrojar algo de luz sobre esto, estaría muy agradecido.

+1

La respuesta que está buscando también fue cubierto en esta pregunta: http://stackoverflow.com/questions/2346763/any-simple-way-to- Explicar por qué no puedo-hacer-listanimal-animals-new-arraylistdo/2346857 # 2346857 –

+0

Sí, gracias - Lo vi en la lista 'relacionada' una vez que publiqué la pregunta, pero no apareció mientras Lo estaba escribiendo ... –

Respuesta

14

¡Esto funcionará en C# 4.0! De lo que estás hablando se llama covarianza genérica.

En el ínterin se puede utilizar el Cast extension method:

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

Ok, brillante, pero realmente no parece ciencia de cohetes, ¿hay alguna razón particular por la que no fue compatible anteriormente? –

+0

@Matt: no es ciencia espacial, pero, como cualquier característica, lleva tiempo y dinero implementarla y ha habido algunas funciones excelentes con mayor prioridad que han implementado antes (por ejemplo, LINQ es realmente bueno, ¿no?). Deberá cortar algunas funciones excelentes para enviar un producto a tiempo. - Una cosa a tener en cuenta acerca de esta característica específica es que requiere soporte del CLR subyacente y CLR 2.0 no lo admite. Soporte agregado en CLR 4.0. –

+0

Gracias - muy útil ... :) –

3

Esto es compatible con .NET 4.0, pero no antes.

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

+1

Técnicamente ha sido compatible con .Net a través de IL desde 2.0, pero solo obtuvo soporte en C# y VB.Net en 4.0 – JaredPar

+0

@JaredPar: ¿De verdad? ¿El CLR 2.0 admite la covarianza genérica en * verificable * IL? –

+0

@Mehrdad, Sí, eso creo. Han pasado más de un año desde que tuve esta conversación con Lucian, pero mi recuerdo actual es que CLR admitía co/contravariancia desde 2.0 y que 4.0 estaba en los compiladores (quizás una corrección de errores o dos en el CLR). – JaredPar

3

Para aclarar, la razón por la que no funciona ahora es porque IEnumerable<MyClass> no hereda de IEnumerable<MyBar>. Lo diré de nuevo: el enumerable del tipo derivado no hereda del enumerable del tipo base. Más bien, ambos tipos se especializan en el tipo genérico IEnumerable<T>. En .Net 3.5 y versiones anteriores, no tienen otra relación más allá de la especialización (que no es herencia) y, por lo demás, son dos tipos completamente diferentes en el sistema de tipos.

.Net 4.0 no cambiará la forma en que estos tipos se relacionan en absoluto. Todavía serán dos tipos completamente diferentes que se relacionan solo a través de la especialización. Lo que hará es permitirle escribir métodos que conozcan la relación de especialización y, en algunos casos, le permiten sustituir uno cuando escribió el otro.

+1

Para aclarar su segundo párrafo: el punto de la varianza genérica es que altera la relación "es compatible con la asignación" en ciertos tipos genéricos. Esa es la relación relevante; la relación de herencia no se modifica, como dices. –

+0

Joel, nunca pensé en la varianza de varianza/contra de esta manera. Respuesta muy útil, +1 de mi parte. – SolutionYogi

1

Si desea realizar un reparto entre IEnumerables (como escribió en el título de su pregunta) y no entre Listas (como escribió en su pregunta) puede esperar la característica de covarianza C# 4.0. Antes de ese día puede usar métodos de extensión. Pero no utilizaría el Cast extension method indicado en otras respuestas, pero escribiendo mi propio método, que solo se puede usar para la conversión de covarianza en IEnumerable. Cuando/Si cambia a C# 4.0, encontrará fácilmente todos los lugares en su código donde el elenco será redundante.

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

Final Node. No parece haber no precompiler constant for the netframework 4.0, de lo contrario me gustaría añadir

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