web-dev-qa-db-fra.com

Qu'est-ce qu'un "type générique ouvert" dans .NET?

Je passais par Leçon Asp.Net MVC et j'ai appris que, pour qu'une méthode soit qualifiée d'action pour un contrôleur,

  • Il ne doit pas avoir un "type générique ouvert"

Je comprends un peu les génériques et les utilise dans une certaine mesure, mais:

  • Qu'est-ce qu'un type générique ouvert dans .Net.
  • Existe-t-il un type générique fermé ?
  • Le type générique ouvert est un terme peu utilisé. Qu'est-ce qui est utilisé/confondu avec cela?
112
Asad Butt

Le langage C # définit un type ouvert comme un type qui est soit un argument de type, soit un type générique défini avec des arguments de type inconnu:

Tous les types peuvent être classés en types ouverts ou fermés. Un type ouvert est un type qui implique des paramètres de type. Plus précisement:

  • Un paramètre de type définit un type ouvert.
  • Un type de tableau est un type ouvert si et seulement si son type d'élément est un type ouvert.
  • Un type construit est un type ouvert si et seulement si un ou plusieurs de ses arguments de type sont un type ouvert . Un type imbriqué construit est un type ouvert si et seulement si un ou plusieurs de ses arguments de type ou les arguments de type de son ou ses types contenant est un Type ouvert.

Un type fermé est un type qui n'est pas un type ouvert.

Par conséquent, T, List<T> Et Dictionary<string,T> Et Dictionary<T,U> Sont tous des types ouverts (T et U sont de type arguments) alors que List<int> et Dictionary<string,int> sont des types fermés.

Il existe un concept connexe: un type générique non lié est un type générique avec des arguments de type non spécifiés. Un type indépendant ne peut pas être utilisé dans des expressions autres que typeof() et vous ne pouvez pas l'instancier ou appeler ses méthodes. Par exemple, List<> Et Dictionary<,> Sont des types non liés.

Pour clarifier la distinction subtile entre un type ouvert et un type non lié:

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

Si vous exécutez cet extrait, il sera imprimé

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

qui est le nom CLR pour List<int>. Il est clair au moment de l'exécution que l'argument type est System.Int32. Cela fait de List<T> Un type ouvert lié .

Au moment de l'exécution, vous pouvez utiliser la réflexion pour lier des arguments de type à des paramètres de type non spécifiés de types génériques non liés avec la méthode Type.MakeGenericType :

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

Vous pouvez vérifier si un type est un type générique non lié ( définition de type générique ) à partir de laquelle vous pouvez construire des types liés avec le Type.IsGenericTypeDefinition Propriété :

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

Pour obtenir le type non lié à partir d'un type construit lors de l'exécution, vous pouvez utiliser la méthode Type.GetGenericTypeDefinition .

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

Notez que pour un type générique, vous pouvez avoir une définition de type complètement indépendante ou une définition complètement liée. Vous ne pouvez pas lier certains paramètres de type et laisser d'autres non liés. Par exemple, vous ne pouvez pas avoir Dictionary<int,> Ou Dictionary<,string>.

186
Mehrdad Afshari

Juste pour ajouter:

Dictionary<string, T> (ou plus précisément Dictionary<string,>) est toujours un type ouvert.

Exemple:

void Foo<T>(Dictionary<string,T> dic) { ... }
9
leppie

Un "type générique ouvert" n'est qu'un type générique dont le type n'est pas encore spécifié (par exemple, CargoCrate<T>). Il devient "fermé" une fois qu'un type de béton a été attribué (par exemple CargoCrate<Widget>).

Par exemple, disons que vous avez quelque chose comme ceci:

public class Basket<T> {
  T[] basketItems;
}

public class PicnicBlanket<T> {
  Basket<T> picnicBasket;   // Open type here. We don't know what T is.
}

                                 // Closed type here: T is Food.
public class ParkPicnicBlanket : PicnicBlanket<Food> {
}

Ici, le type de picnicBasket est ouvert: rien n'a encore été assigné à T. Lorsque vous faites un PicnicBlanket concret avec un type spécifique - par exemple, en écrivant PicnicBlanket<Food> p = new PicnicBlanket<Food>() - nous l'appelons maintenant fermé.

6
John Feminella

Il existe trois types de types génériques. Pour faire court, dans cette déclaration (simplifiée):

public class Dictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
  • Dictionary<TKey, TValue> est un type générique illimité.

  • KeyValuePair<TKey, TValue> est, dans ce cas, un type générique construit ouvert. Il a certains paramètres de type, mais ils sont déjà définis ailleurs (dans Dictionary, dans ce cas).

  • Dictionary<string, int> serait un type générique construit fermé.

5
Gorpik