204 votos

¿Cuáles son las diferencias entre los medicamentos Genéricos en Java y C#... y Plantillas en C++?

Yo sobre todo el uso de Java y los genéricos son relativamente nuevos. Sigo leyendo que Java tomado la decisión equivocada o que .NET tiene mejores implementaciones, etc. etc.

Así que, ¿cuáles son las principales diferencias entre C++, C#, Java en los medicamentos genéricos? Los Pros y los contras de cada uno?

365voto

Orion Edwards Puntos 54939

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

C# Genéricos permite declarar algo como esto.

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

y, a continuación, el compilador le impide poner cosas que no son Person en la lista.
Detrás de las escenas, el compilador de C# es sólo poner List<Person> en el .NET archivo dll, pero en tiempo de ejecución el compilador JIT se va y construye un nuevo conjunto de código, como si los hubiera escrito una lista especial de la clase sólo para contener a la gente - algo como ListOfPerson.

El beneficio de esto es que se hace muy rápido. No hay casting o cualquier otra cosa, y porque la dll que contiene la información que esta es una Lista de Person, otro código que ve a ella más adelante, en el uso de la reflexión puede decir que contiene Person objetos (para obtener intellisense y así sucesivamente).

La desventaja de esto es que las viejas C# 1.0 y 1.1 código (antes de que se agrega genéricos) no entiende estas nuevas List<something>, por lo que usted tiene que convertir manualmente todo vuelve a la llanura de edad List para interactuar con ellos. Esto no es un problema, ya que C# 2.0 código binario no es compatible. La única vez que le suceda esto es que si vas a actualizar algunos de los antiguos C# 1.0/1.1 código de C# 2.0

Java Genéricos permite declarar algo como esto.

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

En la superficie se ve la misma, y que es una especie de es. El compilador también evitará que se pongan cosas que no son Person en la lista.

La diferencia está en lo que sucede detrás de las escenas. A diferencia de C#, Java no ir y construir un especial ListOfPerson - sólo utiliza la llanura de edad ArrayList que siempre ha sido en Java. Cuando usted consigue las cosas fuera de la matriz, la costumbre Person p = (Person)foo.get(1); casting de baile que aún no se ha hecho. El compilador es el ahorro de la clave de las prensas, pero la velocidad hit/casting es todavía incurridos como siempre lo fue.
Cuando la gente menciona "el Tipo de Borrado de" esto es lo que estamos hablando. El compilador inserta los moldes para usted, y luego en 'borra' el hecho de que no pretende ser una lista de Person no solo Object

La ventaja de este enfoque es que el viejo código que no entiende que los genéricos no tienen que cuidar. Todavía tratando de la misma edad ArrayList como siempre. Esto es más importante en el mundo de java porque querían apoyar a la compilación de código mediante el uso de Java 5 con los genéricos, y tener que correr viejos 1.4 o anterior (JVM), que microsoft deliberadamente decidió no molestar.

La desventaja es la velocidad de golpe he mencionado anteriormente, y también porque no hay ListOfPerson de pseudo-clase o algo así, que va en el .class los archivos, el código que ve en ella más adelante (con la reflexión, o si usted tire de ella fuera de otra colección, donde se ha convertido en Object o así) no se puede decir de ninguna manera que esto pretende ser una lista que contiene sólo Person y no cualquier otra lista de matriz.

Las Plantillas de C++ permite declarar algo como esto

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

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

Se tiene más en común con C# genéricos en que se basa especial pseudo-classes en lugar de simplemente tirar el tipo de información como la de java, pero se trata de un conjunto de diferentes hervidor de pescado.

C# y Java producir un resultado que está diseñado para las máquinas virtuales. Si escribes un código que tiene un Person de la clase en que, en ambos casos algo de información sobre un Person clase vaya con el .dll o .class archivo, y la JVM/CLR va a hacer cosas con esto.

C++ produce crudo x86 código binario. Todo lo que es no un objeto, y no hay subyacente de la máquina virtual que necesita saber acerca de un Person de la clase. No hay boxeo o unboxing, y las funciones no tienen que pertenecer a las clases, o de hecho cualquier cosa.

Debido a esto, el compilador de C++ no impone restricciones sobre lo que puede hacer con las plantillas - básicamente cualquier código que usted podría escribir de forma manual, puede obtener plantillas para escribir para usted.
El ejemplo más obvio es la adición de las cosas:

En C# y Java, los genéricos sistema necesita saber qué métodos están disponibles para una clase, y se tiene que pasar esto a la máquina virtual. La única manera de saber que esta es por tanto difícil de codificar la clase real, o el uso de interfaces. Por ejemplo:

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

Ya que el código no compila en C# o Java, ya que no sabe que el tipo T hecho proporciona un método llamado Nombre(). Hay que decir - en C# como esta:

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

Y entonces usted tiene que asegurarse de que las cosas que pasan a addNames implementar el IHasName interfaz y así sucesivamente. La sintaxis de java es diferente (<T extends IHasName>), pero adolece de los mismos problemas.

El 'clásico' caso para que este problema se está tratando de escribir una función que hace esto

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

En realidad no se puede escribir este código porque no hay maneras de declarar una interfaz con el + método. No.

C++ sufre de ninguno de estos problemas. El compilador no le importa que pasa tipos a cualquier VM - si ambos objetos tienen un dominio .Name() la función de compilar. Si no, no. Sencillo.

Así que, ahí lo tienen :-)

61voto

Konrad Rudolph Puntos 231505

C++ utiliza raramente los "genéricos" de la terminología. En cambio, la palabra "plantillas" se utiliza y es más preciso. Las plantillas se describe una técnica para lograr un diseño genérico.

Las plantillas de C++ es muy diferente de lo que C# y Java implementar por dos razones principales. La primera razón es que las plantillas de C++ no sólo permiten en tiempo de compilación el tipo de los argumentos, sino también en tiempo de compilación const argumentos de valor: las plantillas pueden ser dados como números enteros o incluso la función de firmas. Esto significa que usted puede hacer algunas cosas graciosas 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 se utiliza a los otros distinguidos función de las plantillas de C++, es decir, de la plantilla de la especialización. El código define una clase de plantilla, product que tiene un argumento de valor. También define una especialización para que la plantilla que se utiliza cuando el argumento se evalúa a 1. Esto me permite definir una recursión sobre definiciones de la plantilla. Creo que esto fue descubierto por Andrei Alexandrescu.

La plantilla de la especialización es importante para C++, porque permite diferencias estructurales en estructuras de datos. Las plantillas, como un todo, es un medio de unificación de una interfaz a través de los tipos. Sin embargo, aunque esto es conveniente, de todos los tipos no pueden ser tratados de igual manera dentro de la aplicación. Las plantillas de C++ toma esto en cuenta. Esta es la misma diferencia que la programación orientada a objetos hace entre la interfaz y la implementación con el primordial de métodos virtuales.

Las plantillas de C++ son esenciales para su algorítmica paradigma de programación. Por ejemplo, casi todos los algoritmos para los contenedores que se definen como funciones que aceptan el tipo de contenedor como un tipo de plantilla y tratar de manera uniforme. En realidad, eso no es cierto: C++ no funciona en los contenedores, sino en los rangos que se definen por dos iteradores, señalando el principio y detrás de la final del contenedor. Por lo tanto, todo el contenido está circunscrita por los iteradores: begin <= elementos < fin.

El uso de iteradores en lugar de los contenedores es útil porque permite operar sobre las partes de un contenedor en lugar de en el conjunto.

Otra característica distintiva de C++ es la posibilidad de parcial especialización de plantillas de clase. Esto es algo relacionado con la concordancia de patrones en los 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 podemos almacenar punteros más effciently de otros tipos mediante la aplicación de algún truco especial. Podemos hacerlo parcialmente especializada para todos los tipos de puntero:

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

Ahora, cada vez que se instancia un contenedor de la plantilla para un tipo, la definición que se utiliza:

Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.

35voto

jfs Puntos 13605

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

18voto

Jörg W Mittag Puntos 153275

Ya hay un montón de buenas respuestas en lo que las diferencias son, por tanto, déjenme darles una perspectiva ligeramente diferente y agregar el por qué.

Como ya fue explicado, la principal diferencia es el tipo de borrado, es decir. el hecho de que el compilador de Java borra los tipos genéricos y que no acaben en el bytecode generado. Sin embargo, la pregunta es: ¿por qué iba alguien a hacer eso? No tiene sentido! O no?

Bien, ¿cuál es la alternativa? Si usted no aplicar medicamentos genéricos en el idioma, en el que hacer de la implementación? Y la respuesta es: en la Máquina Virtual. Lo que rompe la compatibilidad hacia atrás.

Tipo de borrado, por otro lado, permite la mezcla genérica de los clientes con los no-genérico bibliotecas. En otras palabras: el código compilado en Java 5 todavía puede ser implementado en Java 1.4.

Microsoft, sin embargo, decidió romper la compatibilidad hacia atrás de los medicamentos genéricos. Eso es por qué .NET los medicamentos Genéricos son "mejores" que las de Java Genéricos.

Por supuesto, el Sol no son idiotas o de los cobardes. La razón por la que "acobardado", era que Java fue significativamente mayor y más extendida de lo que .NET cuando se presentó genéricos. (Se introdujeron aproximadamente en el mismo tiempo en ambos mundos). Romper la compatibilidad hacia atrás habría sido un enorme dolor.

Poner aún de otra manera: en Java, los Genéricos son una parte de la Lengua (lo que significa que se aplican sólo a Java, no a otros idiomas), en .NET ellos son parte de la Máquina Virtual (lo que significa que se aplican a todos los lenguajes, no sólo C# y Visual Basic.NET).

Compare esto con .NET características como LINQ, expresiones lambda, la variable local de la inferencia de tipos, tipos anónimos y árboles de expresión: estos son todos los idiomas características. Es por eso que hay sutiles diferencias entre VB.NET C#y: si esas características fueron parte de la VM, que sería el mismo en todos los idiomas. Pero el CLR no ha cambiado: sigue siendo el mismo en .NET 3.5 SP1 como lo fue en .NET 2.0. Usted puede compilar un programa de C# que utiliza LINQ con el .NET 3.5 compilador y todavía se ejecutan en .NET 2.0, siempre que no se utilice cualquier .NET 3.5 de bibliotecas. Que no trabajo con los medicamentos genéricos y de .NET 1.1, pero que iba a trabajar con Java y Java 1.4.

14voto

Konrad Rudolph Puntos 231505

Seguimiento a mi publicación anterior.

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

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 duro para el IDE a decir en ese momento si, y lo que, los miembros de la a tiene. Para otros idiomas, el análisis sería sencillo, pero para C++, un poco de evaluación es necesario de antemano.

Se pone peor. ¿Qué pasa si my_int_type fueron definidos dentro de una clase de plantilla así? Ahora su tipo dependerá de otro tipo de argumento. Y aquí, incluso, los compiladores de fallar.

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

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

Después de un poco de pensamiento, un programador a la conclusión de que este código es el mismo que el de arriba: Y<int>::my_type resuelve int, por lo tanto, b deben ser del mismo tipo como a, ¿verdad?

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

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

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

Y<int>::my_type(123);

Este código afirmación es perfectamente válida y le dice a C++ para ejecutar la llamada a la función Y<int>::my_type. Sin embargo, si my_type no es una función sino un tipo, esta afirmación sigue siendo válido y realizar un yeso especial (la función de estilo cast) que a menudo es un constructor de invocación. El compilador no puede saber lo que queremos decir, entonces tenemos que explicar aquí.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X