O que exatamente é um “tipo genérico aberto” no .NET?

Eu estava passando pela lição Asp.Net MVC e aprendi que, para um método se qualificar como uma ação para um controlador,

  • Não deve ter um “tipo genérico aberto”

Eu entendo um pouco os genéricos e os uso até certo ponto, mas:

  • O que é um tipo genérico aberto em .Net.
  • Existe algo como um tipo genérico fechado ?
  • Tipo genérico aberto é um termo não usado com muita frequência. O que é usado / confuso com isso?

A linguagem C # define um tipo aberto para ser um tipo que é um argumento de tipo ou um tipo genérico definido com argumentos de tipo desconhecido:

Todos os tipos podem ser classificados como tipos abertos ou fechados. Um tipo aberto é um tipo que envolve parâmetros de tipo. Mais especificamente:

  • Um parâmetro de tipo define um tipo aberto.
  • Um tipo de matriz é um tipo aberto se e somente se o tipo de elemento for um tipo aberto.
  • Um tipo construído é um tipo aberto se e somente se um ou mais de seus argumentos de tipo for um tipo aberto . Um tipo nested construído é um tipo aberto se, e somente se, um ou mais de seus argumentos de tipo ou os argumentos de tipo de seu (s) tipo (s) contido (s) for um tipo aberto.

Um tipo fechado é um tipo que não é um tipo aberto.

Portanto, T , List e Dictionary e Dictionary são todos tipos abertos ( T e U são argumentos de tipo), enquanto List e Dictionary são tipos fechados .

Há um conceito relacionado: Um tipo genérico nãoligado é um tipo genérico com argumentos de tipo não especificados. Um tipo não acoplado não pode ser usado em expressões diferentes de typeof() e você não pode instanciá-lo ou chamar seus methods. Por exemplo, List<> e Dictionary< ,> são tipos não vinculados.

Para esclarecer a distinção sutil entre um tipo aberto e um tipo não acoplado:

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

Se você executar este trecho, ele será impresso

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

qual é o nome CLR para List . É claro em tempo de execução que o argumento type é System.Int32 . Isso torna List um tipo aberto acoplado .

Em tempo de execução, você pode usar a reflection para vincular argumentos de tipo a parâmetros de tipos não especificados de tipos genéricos não vinculados com o método Type.MakeGenericType :

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

Você pode verificar se um tipo é um tipo genérico não acoplado ( definição de tipo genérico ) a partir do qual você pode construir tipos vinculados com a propriedade Type.IsGenericTypeDefinition :

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

Para obter o tipo não acoplado de um tipo construído em tempo de execução, você pode usar o método Type.GetGenericTypeDefinition .

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

Observe que, para um tipo genérico, você pode ter uma definição de tipo completamente não acoplada ou uma definição completamente vinculada. Você não pode vincular alguns parâmetros de tipo e deixar outros não vinculados. Por exemplo, você não pode ter o Dictionary ou o Dictionary< ,string> .

Apenas para adicionar:

Dictionary (ou mais precisamente, Dictionary ) ainda é um tipo aberto.

Exemplo:

 void Foo(Dictionary dic) { ... } 

Um “tipo genérico aberto” é apenas um tipo genérico que ainda não tem seu tipo especificado (por exemplo, CargoCrate ). Ele se torna “fechado” quando um tipo concreto é atribuído (por exemplo, CargoCrate ).

Por exemplo, digamos que você tenha algo assim:

 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 { } 

Aqui, o tipo de picnicBasket está aberto: nada foi atribuído a T Quando você faz um PicnicBlanket concreto com um tipo específico – por exemplo, escrevendo PicnicBlanket p = new PicnicBlanket() – agora o chamamos de fechado .

Existem três tipos de tipos genéricos. Para resumir, nesta declaração (simplificada):

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

  • KeyValuePair é, nesse caso, um tipo genérico aberto . Ele possui alguns parâmetros de tipo, mas eles já estão definidos em outro lugar (no Dictionary, neste caso).

  • Dictionary seria um tipo genérico construído fechado .