¿Qué es exactamente un “tipo genérico abierto” en .NET?

Estaba pasando por la lección Asp.Net MVC y aprendí que, para que un método califique como una acción para un controlador,

  • No debe tener un “tipo genérico abierto”

Comprendo algo de generics y los uso hasta cierto punto, pero:

  • Qué es un tipo genérico abierto en .Net.
  • ¿Hay algo así como un tipo genérico cerrado ?
  • El tipo genérico abierto es un término que no se usa con mucha frecuencia. ¿Qué se usa / confunde con eso?

El lenguaje C # define un tipo abierto como un tipo que es un argumento de tipo o un tipo genérico definido con argumentos de tipo desconocido:

Todos los tipos se pueden clasificar como tipos abiertos o cerrados. Un tipo abierto es un tipo que implica parámetros de tipo. Más específicamente:

  • Un parámetro de tipo define un tipo abierto.
  • Un tipo de matriz es un tipo abierto si y solo si su tipo de elemento es de tipo abierto.
  • Un tipo construido es un tipo abierto si y solo si uno o más de sus argumentos de tipo es un tipo abierto . Un tipo nested construido es un tipo abierto si y solo si uno o más de sus argumentos de tipo o los argumentos de tipo de su tipo (s) contiene un tipo abierto.

Un tipo cerrado es un tipo que no es de tipo abierto.

Por lo tanto, T , List y Dictionary , y Dictionary son todos tipos abiertos ( T y U son argumentos de tipo), mientras que List y Dictionary son tipos cerrados .

Hay un concepto relacionado: un tipo genérico no vinculado es un tipo genérico con argumentos de tipo no especificado. Un tipo no enlazado no se puede usar en expresiones que no sean typeof() y no se puede crear una instancia o llamar a sus métodos. Por ejemplo, List<> y Dictionary<,> son tipos independientes.

Para aclarar la distinción sutil entre un tipo abierto y uno no vinculado:

 class Program { static void Main() { Test(); } static void Test() { Console.WriteLine(typeof(List)); // Print out the type name } } 

Si ejecuta este fragmento, se imprimirá

 System.Collections.Generic.List`1[System.Int32] 

que es el nombre CLR para List . Está claro en el tiempo de ejecución que el argumento de tipo es System.Int32 . Esto hace que List un tipo abierto encuadernado .

En tiempo de ejecución, puede usar la reflexión para vincular los argumentos de tipo a los parámetros de tipo no especificado de los tipos generics no Type.MakeGenericType con el método Type.MakeGenericType :

 Type unboundGenericList = typeof(List<>); Type listOfInt = unboundGenericList.MakeGenericType(typeof(int)); if (listOfInt == typeof(List)) Console.WriteLine("Constructed a List type."); 

Puede verificar si un tipo es un tipo genérico no vinculado ( definición de tipo genérico ) a partir del cual puede construir tipos vinculados con la propiedad Type.IsGenericTypeDefinition :

 Console.WriteLine(typeof(Dictionary<,>).IsGenericTypeDefinition); // True Console.WriteLine(typeof(Dictionary).IsGenericTypeDefinition); // False 

Para obtener el tipo independiente de un tipo construido en tiempo de ejecución, puede usar el método Type.GetGenericTypeDefinition .

 Type listOfInt = typeof(List); Type list = listOfInt.GetGenericTypeDefinition(); // == typeof(List<>) 

Tenga en cuenta que para un tipo genérico, puede tener una definición de tipo completamente independiente o una definición completamente enlazada. No puede enlazar algunos parámetros de tipo y dejar otros sin unir. Por ejemplo, no puede tener Dictionary o Dictionary<,string> .

Solo para agregar:

Dictionary (o más precisamente Dictionary ) sigue siendo un tipo abierto.

Ejemplo:

 void Foo(Dictionary dic) { ... } 

Un “tipo genérico abierto” es simplemente un tipo genérico que todavía no tiene su tipo especificado (por ejemplo, CargoCrate ). Se vuelve “cerrado” una vez que se ha asignado un tipo concreto (por ejemplo, CargoCrate ).

Por ejemplo, di que tienes algo como esto:

 public class Basket { T[] basketItems; } public class PicnicBlanket { Basket picnicBasket; // Open type here. We don't know what T is. } // Closed type here: T is Food. public class ParkPicnicBlanket : PicnicBlanket { } 

Aquí, el tipo de picnicBasket está abierto: aún no se ha asignado nada a T Cuando elabora un PicnicBlanket concreto con un tipo específico, por ejemplo, al escribir PicnicBlanket p = new PicnicBlanket() , ahora lo llamamos cerrado .

Hay tres tipos de tipos generics. Para abreviar, en esta statement (simplificada):

 public class Dictionary : IEnumerable> 
  • Dictionary es un tipo genérico ilimitado .

  • KeyValuePair es, en este caso, un tipo genérico abierto construido . Tiene algunos parámetros de tipo, pero ya están definidos en otra parte (en Diccionario, en este caso).

  • Dictionary sería un tipo genérico cerrado y construido .