Pregunta

Genérico Varianza en C # 4.0 se ha implementado de tal manera que es posible escribir lo siguiente sin una excepción (que es lo que sucedería en C # 3.0):

 List<int> intList = new List<int>();
 List<object> objectList = intList; 

[Ejemplo no funcional: Véase la respuesta de Jon Skeet]

Hace poco asistí a una conferencia en la que Jon Skeet dio una excelente visión general de la varianza genérica, pero no estoy seguro de que estoy recibiendo por completo - Entiendo la importancia de la in y out palabras clave cuando se trata de contraindicación y co -variance, pero tengo curiosidad por lo que sucede detrás de las escenas.

¿Qué hace el CLR ver cuando se ejecuta este código? es implícitamente convertir el List<int> a List<object> o es simplemente construido en que ahora podemos convertir entre tipos derivados de tipos de padres?

Fuera de interés, por qué no fue este introdujo en las versiones anteriores y cuál es el beneficio principal - es decir, el uso del mundo real

Más información sobre este para Generic varianza (pero la pregunta es muy anticuado, en busca de bienes, hasta a la fecha de la información)

¿Fue útil?

Solución

No, su ejemplo no funcionaría por tres razones:

  • clases (tales como List<T>) son invariantes; sólo los delegados e interfaces son variante
  • Para varianza para trabajar, la interfaz tiene que usar sólo el parámetro de tipo en una dirección (por contravarianza, hacia fuera para covarianza)
  • Los tipos de valor no se admiten como argumentos de tipo de varianza - lo que no hay converstion de IEnumerable<int> a IEnumerable<object> por ejemplo,

(El código de falla al compilar en C # 3.0 y 4.0 -. No hay excepción)

Así que este lo haría de trabajo:

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

El CLR sólo utiliza la referencia, sin cambios - no se crean nuevos objetos. Así que si se llama a objects.GetType() Seguimos obteniendo List<string>.

Creo que no se ha presentado anteriormente debido a que los diseñadores del lenguaje todavía tenían que trabajar en los detalles de la forma de exponerlo -. Que ha estado en el CLR desde v2

Los beneficios son los mismos que otras veces en las que desea ser capaz de utilizar un tipo como otro. Para utilizar el mismo ejemplo utilicé el sábado pasado, si usted tiene algo implementa IComparer<Shape> de comparar formas por área, que es una locura que no se puede usar eso para ordenar una List<Circle> - si se puede comparar dos formas, que sin duda puede comparar cualquiera de los dos círculos. A partir de C # 4, no habría una conversión contravariant de IComparer<Shape> a IComparer<Circle> por lo que se podría llamar circles.Sort(areaComparer).

Otros consejos

Algunas reflexiones adicionales.

  

¿Qué hace el CLR ver cuando se ejecuta este código

Como han señalado correctamente Jon y otros, que no están haciendo variación en las clases, sólo las interfaces y delegados. Así que en su ejemplo, el CLR no ve nada; que el código no compila. Si se obliga a recopilar suficientes mediante la inserción de los moldes, que se bloquea en tiempo de ejecución con un mal excepción de difusión.

Ahora, todavía es una pregunta razonable preguntarse cómo varianza funciona en segundo plano cuando se hace el trabajo. La respuesta es: la razón por la que se restringe esta referencia a argumentos de tipo de interfaz que se parametrizan y delegar tipos es por lo que no que sucede detrás de las escenas. Cuando usted dice

object x = "hello";

lo que sucede detrás de las escenas es la referencia a la cadena se ha quedado atascado en la variable del tipo de objeto sin modificaciones . Los bits que componen una referencia a una cadena son bits legales para ser una referencia a un objeto, por lo que nada tiene que ocurrir aquí. El CLR simplemente deja de pensar en esos bits se refiere a una cuerda y comienza a pensar en ellos como una referencia a un objeto.

Cuando dice:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = e1;

Lo mismo. No pasa nada. Los bits que hacen una referencia a un empadronador cadena son los mismos que los bits que componen una referencia a un objeto enumerador. Hay un poco más de magia que entra en juego cuando se hace un molde, dice:

IEnumerator<string> e1 = whatever;
IEnumerator<object> e2 = (IEnumerator<object>)(object)e1;

Ahora el CLR debe generar un cheque que E1 realidad no aplicar esa interfaz, y que el registro tiene que ser inteligente sobre el reconocimiento de la varianza.

Pero la razón por la que podemos salir con interfaces de variantes siendo sólo no-op conversiones es ya compatibilidad de asignaciones regulares es de esa manera. ¿Qué va a utilizar para e2?

object z = e2.Current;

que devuelve bits que son una referencia a una cadena. Ya hemos establecido que esas son compatibles con objeto sin cambio.

¿Por qué no se introdujo esta antes? Tuvimos otras características para hacer y un presupuesto limitado.

¿Cuál es la ventaja principio? Que las conversiones de la secuencia de la cadena a la secuencia de objeto "sólo trabajo".

  

Como curiosidad, ¿por qué no fue este   introducido en las versiones anteriores

Las primeras versiones (1.x) de .NET no tenían los genéricos en absoluto, por lo que la varianza genérica estaba muy lejos.

Cabe señalar que, en todas las versiones de .NET, hay matriz de covarianza. Por desgracia, es insegura covarianza:

Apple[] apples = new [] { apple1, apple2 };
Fruit[] fruit = apples;
fruit[1] = new Orange(); // Oh snap! Runtime exception! Can't store an orange in an array of apples!

El co- y contra-varianza en C # 4 es seguro, y evita este problema.

  

¿cuál es la ventaja principal - es decir, verdadera   uso en el mundo?

Muchas veces en código, que está llamando una API espera un tipo amplificada de base (por ejemplo IEnumerable<Base>), pero todo lo que tienes es un tipo de derivado amplificada (por ejemplo IEnumerable<Derived>).

En C # 2 y # 3 C, que había necesidad de convertir manualmente a IEnumerable<Base>, a pesar de que debe "sólo trabajo". Co- y contra-varianza hace que sea "sólo trabajo".

p.s. Totalmente aspira que la respuesta de Skeet está comiendo todos mis puntos rep. Maldito seas, Skeet! :-) parece que está contestado esto antes , sin embargo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top