Una de las principales de motivaciones que se tuvo en mente para implementar generics en la CLI es la performance. Existen dos operaciones que se evitan al utilizar generics. La primera es el boxing, es decir, la conversión de un tipo nativo o un value type a un objeto. El convertir un value type a objeto implica crear un nuevo objeto e insertar el valor dentro de él. La segunda es el casting, que se realiza para convertir un tipo (por ejemplo, string) a un tipo superior (por ejemplo, object). Esta última operación es poco costosa, comparada con el boxing.
Si tenemos un tipo llamado MiLista<T>, el compilador de C# generará una clase llamada MiLista`1 (donde 1 es el número de parámetros genéricos). En tiempo de ejecución, el JIT compilará MiLista`1 al tipo que corresponda utilizar. Si utilizamos MiLista<int>, entonces todas las ocurrencias de T serán reemplazadas por el tipo int y el código estará optimizado para ese tipo.
Por su parte, nuestros amigos de Sun no tuvieron la misma idea cuando crearon su implementación de generics. En su caso, al soportar generics sólo en el lado del compilador Java, las clases -aunque sean genéricas- se crean con el tipo Object o con el tipo declarado en el constraint del genérico. Esa frase suena complicada, pero, veamos un ejemplo. En Java, cuando se compila la clase java.util.ArrayList<?>, en realidad se crea la clase java.util.ArrayList y todas las ocurrencias de ? dentro de la clase son reemplazadas por Object. Si creamos una clase MiPropiaLista<? extends TipoX>, todas las ocurrencias de ? dentro de la clase son reemplazadas por TipoX.
Este escenario nos obligará a realizar casting o boxing donde sea necesario, no ganando en performance con respecto a las listas antiguas (ArrayList no genérica). Continuará...