¿Cuáles son las diferencias entre los genéricos en C# y Java... y las Plantillas en C++? [cerrado]


Utilizo principalmente Java y los genéricos son relativamente nuevos. Sigo leyendo que Java tomó la decisión equivocada o que. NET tiene mejores implementaciones, etc. sucesivamente.

Entonces, ¿cuáles son las principales diferencias entre C++, C#, Java en genéricos? Pros / contras de cada uno?

Author: Shog9, 2008-08-28

13 answers

Voy a añadir mi voz al ruido y tomar una puñalada en hacer las cosas claras:

Los genéricos de C# le permiten declarar algo como esto.

List<Person> foo = new List<Person>();

Y luego el compilador evitará que pongas cosas que no son Person en la lista.
Detrás de las escenas el compilador de C # está simplemente poniendo List<Person> en el archivo dll. NET, pero en tiempo de ejecución el compilador JIT va y construye un nuevo conjunto de código, como si hubiera escrito una clase de lista especial solo para contener personas, algo como ListOfPerson.

El beneficio de esto es que lo hace muy rápido. No hay casting o cualquier otra cosa, y debido a que el dll contiene la información de que esta es una Lista de Person, otro código que lo mira más tarde usando reflexión puede decir que contiene objetos Person (así que obtienes intellisense y así sucesivamente).

La desventaja de esto es que el código antiguo de C# 1.0 y 1.1 (antes de que agregaran genéricos) no entiende estos nuevos List<something>, por lo que debe convertir manualmente las cosas a simplemente viejo List para interoperar con ellos. Esto no es un gran problema, porque el código binario de C# 2.0 no es compatible con versiones anteriores. La única vez que esto sucederá es si está actualizando algún código antiguo de C # 1.0 / 1.1 a C # 2.0

Los genéricos de Java le permiten declarar algo como esto.

ArrayList<Person> foo = new ArrayList<Person>();

En la superficie se ve igual, y más o menos lo es. El compilador también evitará que pongas cosas que no son Person en la lista.

La diferencia es lo que sucede entre bastidores. A diferencia de C#, Java no va y construye un ListOfPerson especial - solo usa el antiguo ArrayList que siempre ha estado en Java. Cuando sacas las cosas de la matriz, el habitual Person p = (Person)foo.get(1); casting-dance todavía tiene que hacerse. El compilador le está ahorrando las pulsaciones de teclas, pero la velocidad de hit/casting todavía se incurre como siempre fue.
Cuando la gente menciona "Tipo Borrado" esto es de lo que están hablando. El compilador inserta los casts por usted ,y luego 'borra' el hecho que está destinado a ser una lista de Person no solo Object

El beneficio de este enfoque es que el código antiguo que no entiende los genéricos no tiene que preocuparse. Sigue lidiando con la misma ArrayList de siempre. Esto es más importante en el mundo de Java porque querían soportar la compilación de código usando Java 5 con genéricos, y que se ejecutara en JVM antiguas 1.4 o anteriores, con las que Microsoft decidió deliberadamente no molestarse.

La desventaja es el golpe de velocidad I mencionado anteriormente, y también porque no hay pseudo-clase ListOfPerson ni nada por el estilo en el .los archivos de clase, el código que lo mira más adelante (con reflexión, o si lo sacas de otra colección donde se ha convertido en Object o así sucesivamente) no pueden decir de ninguna manera que está destinado a ser una lista que contenga solo Person y no cualquier otra lista de matrices.

Las plantillas de C++ le permiten declarar algo como esto

std::list<Person>* foo = new std::list<Person>();

Se parece a los genéricos de C # y Java, y hará lo que usted piensa que debe hacer, pero detrás de las escenas diferentes cosas están sucediendo.

Tiene más en común con los genéricos de C# en que construye especial pseudo-classes en lugar de simplemente tirar la información de tipo a la basura como Java lo hace, pero es una olla de pescado completamente diferente.

Tanto C# como Java producen salida que está diseñada para máquinas virtuales. Si escribes algún código que tenga una clase Person, en ambos casos irá alguna información sobre una clase Person el .dll o .archivo de clase, y el JVM / CLR hará cosas con esto.

C++ produce código binario x86 sin procesar. Todo es no un objeto, y no hay una máquina virtual subyacente que necesite saber acerca de una clase Person. No hay boxeo o unboxing, y las funciones no tienen que pertenecer a clases, o de hecho nada.

Debido a esto, el compilador de C++ no pone restricciones sobre lo que puede hacer con las plantillas - básicamente cualquier código que pueda escribir manualmente, puede obtener plantillas para escribir para ti.
El ejemplo más obvio es agregar cosas:

En C# y Java, el sistema genérico necesita saber qué métodos están disponibles para una clase, y necesita pasar esto a la máquina virtual. La única manera de decirle esto es codificando la clase real o usando interfaces. Por ejemplo:

string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

Ese código no se compilará en C# o Java, porque no sabe que el tipo T realmente proporciona un método llamado Name(). Tienes para decirlo-en C# así:

interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }

Y luego tienes que asegurarte de que las cosas que pasas a addNames implementen la interfaz IHasName y así sucesivamente. La sintaxis de java es diferente (<T extends IHasName>), pero sufre de los mismos problemas.

El caso 'clásico' para este problema es intentar escribir una función que haga esto

string addNames<T>( T first, T second ) { return first + second; }

En realidad no se puede escribir este código porque no hay formas de declarar una interfaz con el método + en ella. Fallaste.

C++ sufre de ninguno de estos problemas. Al compilador no le importa pasar tipos a cualquier máquina virtual - si ambos objetos tienen una función .Name (), se compilará. Si no lo hacen, no lo hará.

Así que, ahí lo tienes: -)

 365
Author: Orion Edwards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-26 03:22:11

C++ rara vez usa la terminología "genérica". En cambio, se usa la palabra "plantillas" y es más precisa. Las plantillas describen una técnica para lograr un diseño genérico.

Las plantillas de C++ son muy diferentes de lo que implementan C# y Java por dos razones principales. La primera razón es que las plantillas de C++ no solo permiten argumentos de tipo en tiempo de compilación, sino también argumentos de valor de const en tiempo de compilación: las plantillas se pueden dar como enteros o incluso firmas de función. Esto significa que usted puede hacer algunas cosas bastante funky en tiempo de compilación, por ejemplo, cálculos:

template <unsigned int N>
struct product {
    static unsigned int const VALUE = N * product<N - 1>::VALUE;
};

template <>
struct product<1> {
    static unsigned int const VALUE = 1;
};

// Usage:
unsigned int const p5 = product<5>::VALUE;

Este código también utiliza la otra característica distinguida de las plantillas de C++, a saber, la especialización de plantillas. El código define una plantilla de clase, product que tiene un argumento de valor. También define una especialización para esa plantilla que se usa cuando el argumento se evalúa como 1. Esto me permite definir una recursión sobre definiciones de plantilla. Creo que esto fue descubierto por primera vez por Andrei Alexandrescu .

La especialización de plantillas es importante para C++ porque permite diferencias estructurales en estructuras de datos. Las plantillas como un todo son un medio para unificar una interfaz entre tipos. Sin embargo, aunque esto es deseable, todos los tipos no pueden ser tratados por igual dentro de la implementación. Las plantillas de C++ tienen esto en cuenta. Esta es la misma diferencia que hace OOP entre la interfaz y la implementación con la anulación de los métodos virtuales.

C++ las plantillas son esenciales para su paradigma de programación algorítmica. Por ejemplo, casi todos los algoritmos para contenedores se definen como funciones que aceptan el tipo de contenedor como un tipo de plantilla y los tratan de manera uniforme. En realidad, eso no es del todo correcto: C++ no funciona en contenedores, sino más bien en rangos que están definidos por dos iteradores, apuntando al principio y detrás del final del contenedor. Por lo tanto, todo el contenido está circunscrito por los iteradores: begin

Usar iteradores en lugar de contenedores es útil porque permite operar en partes de un contenedor en lugar de en el conjunto.

Otra característica distintiva de C++ es la posibilidad de especialización parcial para plantillas de clases. Esto está algo relacionado con la coincidencia de patrones en argumentos en Haskell y otros lenguajes funcionales. Por ejemplo, consideremos una clase que almacena elementos:

template <typename T>
class Store { … }; // (1)

Esto funciona para cualquier tipo de elemento. Pero digamos que puede almacenar punteros de forma más eficaz que otros tipos mediante la aplicación de algún truco especial. Podemos hacer esto por parcialmente especializándonos para todos los tipos de puntero:

template <typename T>
class Store<T*> { … }; // (2)

Ahora, cada vez que instigamos una plantilla contenedor para un tipo, se usa la definición apropiada:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.
 61
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-19 01:29:33

Anders Hejlsberg describió las diferencias aquí " Genéricos en C#, Java y C++".

 35
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 05:14:53

Ya hay muchas buenas respuestas sobre cuáles son las diferencias, así que permítanme dar una perspectiva ligeramente diferente y agregar el por qué.

Como ya se explicó, la principal diferencia es type erasure, es decir, el hecho de que el compilador Java borra los tipos genéricos y no terminan en el bytecode generado. Sin embargo, la pregunta es: ¿por qué alguien haría eso? No tiene sentido! O no?

Bueno, ¿cuál es la alternativa? Si no implementar genéricos en el lenguaje, donde hacer implementarlos? Y la respuesta es: en la Máquina Virtual. Lo que rompe la compatibilidad hacia atrás.

Type erasure, por otro lado, le permite mezclar clientes genéricos con bibliotecas no genéricas. En otras palabras: el código compilado en Java 5 todavía se puede implementar en Java 1.4.

Microsoft, sin embargo, decidió romper la compatibilidad hacia atrás para genéricos. Por eso Los genéricos. NET son" mejores " que Java Genéricos.

Por supuesto, Sun no son idiotas o cobardes. La razón por la que "se acobardaron", fue que Java era significativamente más antiguo y más extendido que.NET cuando introdujeron genéricos. (Fueron introducidos aproximadamente al mismo tiempo en ambos mundos. Romper la compatibilidad hacia atrás habría sido un gran dolor.

Dicho de otra manera: en Java, los genéricos son parte del Lenguaje (lo que significa que se aplican solo a Java, no a otros lenguajes), en. NET son parte de la Máquina Virtual (lo que significa que se aplican a todos los lenguajes, no solo C# y Visual Basic.NET).

Compare esto con características de.NET como LINQ, expresiones lambda, inferencia de tipos de variables locales, tipos anónimos y árboles de expresiones: todas estas son características de language. Es por eso que hay diferencias sutiles entre VB.NET y C#: si esas características fueran parte de la VM, serían las mismas en todos los lenguajes. Pero el CLR no ha cambiado: sigue siendo el mismo en. NET 3.5 SP1 que en. NET 2.0. Puede compilar un programa de C # que use LINQ con el compilador. NET 3.5 y aún ejecutarlo en. NET 2.0, siempre que no use ninguna biblioteca. NET 3.5. Eso no funcionaría con genéricos y.NET 1.1, pero funcionaría con Java y Java 1.4.

 18
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-29 01:08:19

Seguimiento a mi publicación anterior.

Las plantillas son una de las principales razones por las que C++ falla tan abismalmente en intellisense, independientemente del IDE utilizado. Debido a la especialización de plantillas, el IDE nunca puede estar realmente seguro de si un miembro determinado existe o no. Considere:

template <typename T>
struct X {
    void foo() { }
};

template <>
struct X<int> { };

typedef int my_int_type;

X<my_int_type> a;
a.|

Ahora, el cursor está en la posición indicada y es muy difícil para el IDE decir en ese momento si, y qué, los miembros a tienen. Para otros lenguajes el análisis sería sencillo, pero para C++, se necesita un poco de evaluación de antemano.

Se pone peor. ¿Qué pasaría si my_int_type se definiera también dentro de una plantilla de clase? Ahora su tipo dependería de otro argumento de tipo. Y aquí, incluso los compiladores fallan.

template <typename T>
struct Y {
    typedef T my_type;
};

X<Y<int>::my_type> b;

Después de pensar un poco, un programador concluiría que este código es el mismo que el anterior: Y<int>::my_type resuelve int, por lo tanto b debería ser del mismo tipo que a, ¿verdad?

Incorrecto. En el punto donde el compilador intenta resolver esto declaración, en realidad no sabe Y<int>::my_type todavía! Por lo tanto, no sabe que este es un tipo. Podría ser otra cosa, por ejemplo, una función miembro o un campo. Esto podría dar lugar a ambigüedades (aunque no en el presente caso), por lo tanto el compilador falla. Tenemos que decirle explícitamente que nos referimos a un nombre de tipo:

X<typename Y<int>::my_type> b;

Ahora, el código compila. Para ver cómo surgen ambigüedades de esta situación, considere el siguiente código:

Y<int>::my_type(123);

Esta declaración de código es perfectamente valid y le dice a C++ que ejecute la llamada a la función Y<int>::my_type. Sin embargo, si my_type no es una función sino más bien un tipo, esta instrucción seguiría siendo válida y realizaría un cast especial (el cast de estilo función) que a menudo es una invocación de constructor. El compilador no puede decir a qué nos referimos, así que tenemos que desambiguar aquí.

 14
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-01 09:21:04

Tanto Java como C# introdujeron genéricos después de su primer lanzamiento en lenguaje. Sin embargo, hay diferencias en cómo las bibliotecas centrales cambiaron cuando se introdujo generics. Los genéricos de C#no son solo magia del compilador y por lo tanto no era posible generalizar las clases de bibliotecas existentes sin romper la compatibilidad con versiones anteriores.

Por ejemplo, en Java el Framework de Colecciones existente fue completamente genérico. Java no tiene ambos versión genérica y legacy no genérica de las clases collections. De alguna manera esto es mucho más limpio: si necesita usar una colección en C#, realmente hay muy pocas razones para ir con la versión no genérica, pero esas clases heredadas permanecen en su lugar, saturando el paisaje.

Otra diferencia notable son las clases Enum en Java y C#. La enumeración de Java tiene esta definición un tanto tortuosa:

//  java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

(ver muy claro de Angelika Langer explicación de exactamente por qué esto es así. Esencialmente, esto significa que Java puede dar acceso seguro de tipo desde una cadena a su valor Enum:

//  Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");

Compare esto con la versión de C#:

//  Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");

Como Enum ya existía en C# antes de que los genéricos se introdujeran en el lenguaje, la definición no podía cambiar sin romper el código existente. Así, al igual que las colecciones, permanece en las bibliotecas centrales en este estado heredado.

 6
Author: serg10,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-30 00:50:23

11 meses de retraso, pero creo que esta pregunta está lista para algunas cosas Comodín Java.

Esta es una característica sintáctica de Java. Supongamos que tiene un método:

public <T> void Foo(Collection<T> thing)

Y supongamos que no necesita referirse al tipo T en el cuerpo del método. Estás declarando un nombre T y luego solo usándolo una vez, así que ¿por qué deberías tener que pensar en un nombre para él? En su lugar, puede escribir:

public void Foo(Collection<?> thing)

El signo de interrogación le pide al compilador que pretenda que ha declarado un parámetro de tipo con nombre normal eso sólo tiene que aparecer una vez en ese lugar.

No hay nada que puedas hacer con comodines que no puedas hacer también con un parámetro de tipo con nombre (que es como estas cosas se hacen siempre en C++ y C#).

 4
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-10 13:49:57

Wikipedia tiene excelentes escritos que comparan las plantillas Java/C# generics y Java generics/C++. El artículo principal sobre Genéricos parece un poco desordenado, pero tiene algo de buena información en él.

 2
Author: travis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 05:14:19

La mayor queja es el tipo erasure. En ese caso, los genéricos no se aplican en tiempo de ejecución. Aquí hay un enlace a algunos documentos de Sun sobre el tema.

Los genéricos se implementan por tipo borrado: la información de tipo genérico es presente solo en tiempo de compilación, después que es borrado por el compilador.

 1
Author: Matt Cummings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 05:15:45

Las plantillas de C++ son en realidad mucho más potentes que sus contrapartes de C# y Java, ya que se evalúan en tiempo de compilación y especialización de soporte. Esto permite la Metaprogramación de Plantillas y hace que el compilador de C++ sea equivalente a una máquina de Turing (es decir, durante el proceso de compilación puede computar cualquier cosa que sea computable con una máquina de Turing).

 1
Author: On Freund,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 06:32:49

En Java, los genéricos son solo a nivel de compilador, por lo que se obtiene:

a = new ArrayList<String>()
a.getClass() => ArrayList

Tenga en cuenta que el tipo de 'a' es una lista de matrices, no una lista de cadenas. Así que el tipo de una lista de bananas sería igual a () una lista de monos.

, por Así decirlo.

 1
Author: izb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-28 07:22:31

Parece que, entre otras propuestas muy interesantes, hay una sobre refinar los genéricos y romper la compatibilidad hacia atrás:

Actualmente, los genéricos están implementados el uso de borrado, lo que significa que el la información genérica de tipo no es disponible en tiempo de ejecución, lo que hace que algunos es un código difícil de escribir. Medicamentos Genéricos se implementaron de esta manera para apoyar compatibilidad con versiones anteriores código no genérico. Genéricos reificados haría el tipo genérico información disponible en tiempo de ejecución, lo que rompería el legado no genérico codificar. Sin embargo, Neal Gafter ha propuesta de hacer que los tipos solo sean reificables si se especifica, para no romper compatibilidad con versiones anteriores.

En El artículo de Alex Miller sobre las propuestas de Java 7

 1
Author: pek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-23 07:36:09

NB: No tengo suficiente punto para comentar, así que siéntase libre de mover esto como un comentario a la respuesta apropiada.

Contrariamente a la creencia popular, que nunca entiendo de dónde vino,. net implementó genéricos verdaderos sin romper la compatibilidad con versiones anteriores, y gastaron un esfuerzo explícito para eso. No tiene que cambiar su código. net 1.0 no genérico a genéricos solo para ser utilizado en.net 2.0. Tanto las listas genéricas como las no genéricas siguen estando disponibles en. Net framework 2.0 even hasta 4.0, exactamente por nada más que por razones de compatibilidad con versiones anteriores. Por lo tanto, los códigos antiguos que todavía usaban ArrayList no genérico seguirán funcionando, y usarán la misma clase ArrayList que antes. La compatibilidad con el código anterior siempre se mantiene desde la versión 1.0 hasta ahora... Así que incluso en. net 4.0, todavía tiene la opción de usar cualquier clase no genérica de 1.0 BCL si elige hacerlo.

Así que no creo que Java tenga que romper la compatibilidad con versiones anteriores para soportar genéricos verdaderos.

 0
Author: Sheepy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-03 18:20:58