Reificación significa generalmente (fuera de la informática) "hacer algo real".
En programación, algo se reifica si podemos acceder a la información en el lenguaje mismo.
Para dos ejemplos completamente no relacionados con genéricos de algo que C # hace y no ha reificado, tomemos métodos y acceso a la memoria.
Los lenguajes OO generalmente tienen métodos (y muchos que no tienen funciones similares, aunque no están vinculadas a una clase). Como tal, puede definir un método en dicho lenguaje, llamarlo, quizás anularlo, etc. No todos estos lenguajes le permiten manejar el método en sí mismo como datos para un programa. C # (y, en realidad, .NET en lugar de C #) le permite utilizar MethodInfo
objetos que representan los métodos, por lo que en C # los métodos se reifican. Los métodos en C # son "objetos de primera clase".
Todos los lenguajes prácticos tienen algunos medios para acceder a la memoria de una computadora. En un lenguaje de bajo nivel como C podemos tratar directamente con el mapeo entre direcciones numéricas utilizadas por la computadora, por lo que los gustos int* ptr = (int*) 0xA000000; *ptr = 42;
son razonables (siempre y cuando tengamos una buena razón para sospechar que el acceso a la dirección de memoria 0xA000000
de esta manera no ganó '' t explotar algo). En C # esto no es razonable (podemos forzarlo en .NET, pero con la gestión de memoria de .NET moviendo las cosas no es muy probable que sea útil). C # no tiene direcciones de memoria reified.
Entonces, como refinado significa "hecho real", un "tipo reificado" es un tipo del que podemos "hablar" en el idioma en cuestión.
En genéricos esto significa dos cosas.
Una es que List<string>
es un tipo tal como string
o int
son. Podemos comparar ese tipo, obtener su nombre e investigar al respecto:
Console.WriteLine(typeof(List<string>).FullName); // System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Console.WriteLine(typeof(List<string>) == (42).GetType()); // False
Console.WriteLine(typeof(List<string>) == Enumerable.Range(0, 1).Select(i => i.ToString()).ToList().GetType()); // True
Console.WriteLine(typeof(List<string>).GenericTypeArguments[0] == typeof(string)); // True
Una consecuencia de esto es que podemos "hablar" sobre los tipos de parámetros de un método genérico (o método de una clase genérica) dentro del propio método:
public static void DescribeType<T>(T element)
{
Console.WriteLine(typeof(T).FullName);
}
public static void Main()
{
DescribeType(42); // System.Int32
DescribeType(42L); // System.Int64
DescribeType(DateTime.UtcNow); // System.DateTime
}
Como regla general, hacer esto demasiado es "maloliente", pero tiene muchos casos útiles. Por ejemplo, mira:
public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
Comparer<TSource> comparer = Comparer<TSource>.Default;
TSource value = default(TSource);
if (value == null)
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
do
{
if (!e.MoveNext()) return value;
value = e.Current;
} while (value == null);
while (e.MoveNext())
{
TSource x = e.Current;
if (x != null && comparer.Compare(x, value) < 0) value = x;
}
}
}
else
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (!e.MoveNext()) throw Error.NoElements();
value = e.Current;
while (e.MoveNext())
{
TSource x = e.Current;
if (comparer.Compare(x, value) < 0) value = x;
}
}
}
return value;
}
Esto no hace muchas comparaciones entre el tipo de TSource
y varios tipos para diferentes comportamientos (generalmente una señal de que no debería haber utilizado genéricos en absoluto), pero se divide entre una ruta de código para los tipos que pueden ser null
(debería devolver null
si no se encuentra ningún elemento, y no debe hacer comparaciones para encontrar el mínimo si uno de los elementos comparados es null
) y la ruta del código para los tipos que no se pueden encontrar null
(debe arrojar si no se encuentra ningún elemento, y no tiene que preocuparse por la posibilidad de null
elementos )
Debido a que TSource
es "real" dentro del método, esta comparación se puede hacer en tiempo de ejecución o tiempo de jitting (generalmente tiempo de jitting, ciertamente el caso anterior lo haría en el momento de jit y no produciría código de máquina para la ruta no tomada) y tenemos un versión "real" separada del método para cada caso. (Aunque como una optimización, el código de máquina se comparte para diferentes métodos para diferentes parámetros de tipo de tipo de referencia, porque puede ser sin afectar esto y, por lo tanto, podemos reducir la cantidad de código de máquina jitted).
(No es común hablar sobre la reificación de tipos genéricos en C # a menos que también se trate de Java, porque en C # simplemente damos por sentado esta reificación; todos los tipos están reificados. En Java, los tipos no genéricos se denominan reified porque eso es una distinción entre ellos y los tipos genéricos).