Compartir a través de


Genéricos en tiempo de ejecución (guía de programación de C#)

Cuando se compila un tipo o método genérico en el lenguaje intermedio común (CIL), contiene metadatos que lo identifican como poseedor de parámetros de tipo. La forma en que se usa CIL para un tipo genérico depende de si el parámetro de tipo proporcionado es un tipo de valor o de referencia.

Cuando se construye por primera vez un tipo genérico con un tipo de valor como parámetro, el motor de ejecución crea un tipo genérico especializado sustituyendo el parámetro o los parámetros proporcionados en los lugares adecuados del CIL. Los tipos genéricos especializados se crean una vez para cada tipo de valor único que se usa como parámetro.

Por ejemplo, suponga que el código de su programa ha declarado una pila compuesta por enteros:

Stack<int>? stack;

En este momento, el motor de ejecución genera una versión especializada de la clase Stack<T> sustituyendo el entero adecuadamente para su parámetro. Ahora, cada vez que el código de su programa use una pila de enteros, el motor en tiempo de ejecución vuelve a usar la clase especializada generada Stack<T>. En el ejemplo siguiente, se crean dos instancias de una pila de enteros y comparten una instancia única del código Stack<int>:

Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();

En cambio, suponga que otra clase Stack<T> con un tipo de valor diferente, como un long o una estructura definida por el usuario como parámetro, se crea en otro punto del código. Como resultado, el motor de ejecución genera otra versión del tipo genérico y sustituye un long en los lugares apropiados en el CIL. Las conversiones ya no son necesarias porque cada clase genérica especializada contiene de forma nativa el tipo de valor.

Los genéricos funcionan de forma ligeramente distinta para los tipos de referencia. Cuando se construye un tipo genérico por primera vez con un tipo de referencia, el motor en tiempo de ejecución crea un tipo genérico especializado sustituyendo las referencias a objetos para los parámetros del CIL. Después, cada vez que se crea una instancia de un tipo construido con un tipo de referencia como parámetro, independientemente del tipo que sea, el motor de ejecución vuelve a usar la versión especializada del tipo genérico previamente creada. Esto es posible porque todas las referencias son del mismo tamaño.

Por ejemplo, suponga que tiene dos tipos de referencia, una clase Customer y una clase Order, y que ha creado una pila de tipos Customer:

class Customer { }
class Order { }
Stack<Customer> customers;

En este punto, el motor de ejecución genera una versión especializada de la clase Stack<T> que, en lugar de almacenar los datos, almacena referencias a objetos que se rellenarán más tarde. Suponga que la línea siguiente de código crea una pila de otro tipo de referencia, que se denomina Order:

Stack<Order> orders = new Stack<Order>();

A diferencia de lo que sucede con los tipos de valor, no se crea otra versión especializada de la clase Stack<T> para el tipo Order. En su lugar, se crea una instancia de la versión especializada de la clase Stack<T> y se establece la variable orders para hacer referencia a ella. Suponga que encuentra una línea de código para crear una pila de tipo Customer:

customers = new Stack<Customer>();

Como con el uso anterior de la clase Stack<T> creada usando el tipo Order, se crea otra instancia de la clase especializada Stack<T>. Los punteros contenidos allí se establecen para hacer referencia a un área de memoria del tamaño de un tipo Customer. Dado que el número de tipos de referencia puede variar significativamente de un programa a otro, la implementación de genéricos de C# reduce significativamente la cantidad de código limitando a uno el número de clases especializadas creadas por el compilador para las clases genéricas de tipos de referencia.

Además, cuando se crea una instancia de una clase de C# genérica mediante un parámetro de tipo de valor o de referencia se puede consultar en tiempo de ejecución mediante reflexión, y se puede comprobar tanto su tipo real como su parámetro de tipo.

Consulte también