c# c#-4.0 generics covariance

c# - ¿Por qué ToList<Interface> no funciona para los tipos de valor?



c#-4.0 generics (1)

Si implemento una interfaz para un tipo de valor e intento convertirlo en una lista de su tipo de interfaz, ¿por qué se produce un error mientras que el tipo de referencia se convierte perfectamente?

Este es el error:

No se puede convertir el argumento de instancia escriba System.Collections.Generic.List<MyValueType> en System.Collections.Generic.IEnumerable<MyInterfaceType>

Tengo que usar explícitamente el método Cast<T> para convertirlo, ¿por qué? Como IEnumerable es una enumeración de solo lectura a través de una colección, no tiene ningún sentido para mí que no se pueda transmitir directamente.

Aquí hay un código de ejemplo para demostrar el problema:

public interface I{} public class T : I{} public struct V: I{} public void test() { var listT = new List<T>(); var listV = new List<V>(); var listIT = listT.ToList<I>(); //OK var listIV = listV.ToList<I>(); //FAILS to compile, why? var listIV2 = listV.Cast<I>().ToList(); //OK }


La varianza (covarianza o contravarianza) no funciona para tipos de valores, solo tipos de referencia:

La varianza se aplica solo a los tipos de referencia; si especifica un tipo de valor para un parámetro de tipo de variante, ese parámetro de tipo es invariante para el tipo construido resultante. (MSDN)

Los valores contenidos dentro de las variables de tipo de referencia son referencias (por ejemplo, direcciones) y las direcciones de datos tienen el mismo tamaño y se interpretan de la misma manera, sin ningún cambio requerido en sus patrones de bits.

Por el contrario, los valores que figuran dentro de las variables de tipo de valor no tienen el mismo tamaño o la misma semántica. Usarlos como tipos de referencia requiere que el boxeo y el boxeo requieran instrucciones específicas del tipo para ser emitidas por el compilador. No es práctico o eficiente (a veces tal vez ni siquiera posible) para el compilador emitir instrucciones de boxeo para cualquier tipo posible de tipo de valor, por lo tanto, la variación no se permite por completo.

Básicamente, la varianza es práctica gracias a la capa adicional de indirección (la referencia) de la variable a los datos reales. Debido a que los tipos de valor carecen de esa capa de indirección, carecen de capacidades de varianza.

Combina lo anterior con el funcionamiento de las operaciones LINQ:

Una operación Cast remite / empaqueta todos los elementos (accediendo a ellos a través del IEnumerable no genérico, como señaló) y luego verifica que todos los elementos en una secuencia se pueden convertir / desempaquetar con éxito al tipo proporcionado y luego hace exactamente eso. La operación ToList enumera la secuencia y devuelve una lista de esa enumeración.

Cada uno tiene su propio trabajo. Si (digamos) ToList hizo el trabajo de ambos, tendría la sobrecarga de rendimiento de ambos, lo cual es indeseable para la mayoría de los demás casos.