son sharp que parecidos mas español entre dificil diferencias aprender c# java generics variance

sharp - ¿Cómo se compara la varianza del sitio de uso de Java con la varianza del sitio de declaración de C#?



java vs c++ español (4)

Entiendo que la especificación de la varianza para los genéricos en C # ocurre en el nivel de declaración de tipo: cuando está creando su tipo genérico, especifica la varianza para los argumentos de tipo. En Java, por otro lado, se especifica la varianza donde se usa un genérico: cuando se crea una variable de algún tipo genérico, se especifica cómo pueden variar sus argumentos de tipo.

¿Cuáles son los pros y los contras de cada opción?


Java: genéricos de varianza del sitio de uso desde Java 5. Matrices covariantes rotas con una sintaxis diferente desde 1.0. No hay información de tipo de tiempo de ejecución de genéricos.

C #: genéricos de varianza del sitio de uso desde C # 2.0. Se agregó una variación del sitio de declaración en C # 4.0. Matrices covariantes rotas con una sintaxis diferente desde 1.0 (problema idéntico a Java). genéricos "reificados", lo que significa que la información del tipo no se pierde en el momento de la compilación.

Scala: Tanto la varianza del sitio de uso como del sitio de declaración desde las primeras versiones del lenguaje (al menos desde 2008). Las matrices no son una función de idioma separada, por lo que utiliza la misma sintaxis genérica y las reglas de varianza de tipo. Ciertas colecciones se implementan en el nivel de máquina virtual con matrices JVM para que obtenga un rendimiento igual o mejor en tiempo de ejecución en comparación con el código Java.

Para profundizar en el problema de seguridad del tipo de matriz C # / Java: puede convertir un Perro [] en un Pet [] y agregar un Cat y desencadenar un error de tiempo de ejecución que no se detecta en el tiempo de compilación. Scala lo implementó correctamente.


La mayoría de la gente parece preferir la varianza del sitio de declaración, porque hace que sea más fácil para los usuarios de la biblioteca (al tiempo que lo hace un poco más difícil para el desarrollador de la biblioteca, aunque yo sostengo que el desarrollador de la biblioteca debe pensar en la varianza independientemente en realidad está escrito.)

Pero tenga en cuenta que ni Java ni C # son ejemplos de un buen diseño del lenguaje.

Si bien Java tiene la varianza correcta y funciona independientemente de la JVM debido a las mejoras de VM compatibles en Java 5 y borrado de tipo, la varianza del sitio de uso hace que el uso sea un poco engorroso y la implementación particular de borrado de tipo ha merecido una merecida crítica.

El modelo de la varianza del sitio de declaración de C # quita la carga al usuario de la biblioteca, pero durante la introducción de los genéricos reificados básicamente construyeron las reglas de varianza en su máquina virtual. Incluso hoy en día no pueden respaldar completamente la co / contravariancia debido a este error (y la introducción no compatible con versiones anteriores de las clases de recolección reificadas ha dividido a los programadores en dos campos).

Esto plantea una restricción difícil en todos los lenguajes dirigidos al CLR y es una de las razones por la cual los lenguajes de programación alternativos son mucho más activos en la JVM, aunque parece que el CLR tiene "características mucho más agradables".

Echemos un vistazo a Scala : Scala es un híbrido funcional completamente orientado a objetos que se ejecuta en la JVM. Usan borrado de tipos como Java, pero tanto la implementación de Generics como la varianza (declaratoria) son más fáciles de entender, más directas y poderosas que las de Java (o C # ''s), porque la VM no impone reglas sobre cómo debe hacerlo la varianza. trabajo. El compilador de Scala comprueba las notaciones de varianza y puede rechazar el código fuente incorrecto en tiempo de compilación en lugar de lanzar excepciones en el tiempo de ejecución, mientras que los archivos .class resultantes se pueden usar sin problemas desde Java.

Una desventaja de la varianza del sitio de declaración es que parece hacer que la inferencia de tipo sea más difícil en algunos casos.

Al mismo tiempo, Scala puede usar tipos primitivos sin encajonarlos en colecciones como en C # utilizando la anotación @specialized que le dice al compilador de Scala que genere una o más implementaciones adicionales de una clase o método especializado para el tipo primitivo solicitado.

Scala también puede "casi" reificar genéricos mediante el uso de manifiestos que les permite recuperar los tipos genéricos en tiempo de ejecución como en C #.


Solo voy a responder a las diferencias entre el sitio de declaración y la varianza del sitio de uso, ya que, aunque los genéricos de C # y Java difieren de muchas otras maneras, esas diferencias son en su mayoría ortogonales a la varianza.

Primero, si recuerdo correctamente, la varianza del sitio de uso es estrictamente más poderosa que la varianza del sitio de declaración (aunque a costa de la concisión), o al menos los comodines de Java son (que en realidad son más poderosos que la varianza del sitio de uso). Este aumento de potencia es particularmente útil para los lenguajes en los que las construcciones con estado se usan mucho, como C # y Java (pero Scala lo es mucho menos, especialmente porque sus listas estándar son inmutables). Considere la List<E> (o IList<E> ). Como tiene métodos para agregar E y obtener E, es invariable con respecto a E, por lo que la varianza del sitio de declaración no se puede usar. Sin embargo, con la varianza del sitio de uso, puede decir List<+Number> para obtener el subconjunto covariante de List y List<-Number> para obtener el subconjunto contravariante de List . En un lenguaje de declaración del sitio, el diseñador de la biblioteca tendría que hacer interfaces separadas (o clases si permite herencia múltiple de clases) para cada subconjunto y hacer que List amplíe esas interfaces. Si el diseñador de la biblioteca no hace esto (obsérvese que IEnumerable C # solo hace un pequeño subconjunto de la parte covariante de IList ), entonces no tiene suerte y debe recurrir a las mismas molestias que tiene que hacer en un idioma sin cualquier tipo de varianza

Así que esa es la ventaja de la herencia del sitio de uso sobre la herencia del sitio de declaración. La ventaja de la herencia del sitio de declaración sobre la herencia del sitio de uso es básicamente concisa para el usuario (siempre que el diseñador haya realizado el esfuerzo de separar cada clase / interfaz en sus porciones covariantes y contravariantes). Para algo como IEnumerable o IEnumerable , es bueno no tener que especificar la covarianza cada vez que utiliza la interfaz. Java lo hizo especialmente molesto al usar una sintaxis larga (a excepción de la bivariancia para la cual la solución de Java es básicamente ideal).

Por supuesto, estas dos características del lenguaje pueden coexistir. Para los parámetros de tipo que son naturalmente covariantes o contravariantes (como en IEnumerable / IEnumerable ), declararlo en la declaración. Para los parámetros de tipo que son naturalmente invariables (como en la (I)List ), declare qué tipo de varianza desea cada vez que la use. Simplemente no especifique una varianza del sitio de uso para los argumentos con una varianza del sitio de declaración ya que eso hace que las cosas sean confusas.

Hay otros problemas más detallados que no he investigado (como por ejemplo, cómo los comodines son en realidad más poderosos que la varianza del sitio de uso), pero espero que esto responda a su pregunta sobre su contenido. Admitiré que soy parcial hacia la varianza del sitio de uso, pero intenté describir las principales ventajas de ambos que surgieron en mis discusiones con los programadores y con los investigadores del lenguaje.


Desventajas de los genéricos de estilo Java

Una consecuencia es que la versión java solo funciona con tipos de referencia (o tipos de valores encuadrados) y no con tipos de valores. OMI es la mayor desventaja ya que previene los genéricos de alto rendimiento en muchos escenarios y requiere la escritura manual de tipos especializados.

No garantiza un invariante como "Esta lista solo contiene objetos de tipo x" y necesita verificaciones de tiempo de ejecución en cada getter. El tipo genérico realmente existe.

Al usar la reflexión, no puede preguntar a una instancia de un objeto genérico qué parámetros genéricos tiene.

Ventajas de los genéricos de estilo Java

Obtiene varianza / puede emitir entre diferentes parámetros genéricos.