Maison > Article > développement back-end > Programmation générique C#
Génériques : utilisez des types paramétrés pour utiliser plusieurs types de données sur le même code. Utilisez des « types paramétrés » pour extraire les types afin d'obtenir une réutilisation flexible.
Exemple de code :
class PRogram
{
static void Main(string[] args)
{
int obj = 2;
Test
Console.WriteLine("int:" test.obj);
string obj2 = "hello world";
Test1 = new Test
Console.WriteLine("String:" test1. obj) ;
Console.Read();
}
}
classe Test
{
public T obj;
public Test(T obj)
{
this.obj = obj;
}
}
Le résultat de sortie est :
int:2
String:hello world
Analyse du programme :
1. Test est une classe générique. T est le type générique à instancier. Si T est instancié en tant que type int, alors la variable membre obj est de type int. Si T est instancié en tant que type string, alors obj est de type string.
2. Selon le type, le programme ci-dessus affiche des valeurs différentes.
Mécanisme générique C# :
Les capacités génériques C# sont prises en charge par CLR au moment de l'exécution : le code générique C# utilise des méthodes spéciales lors de la compilation en code IL et en métadonnées. Les espaces réservés sont utilisés pour représenter les types génériques et les opérations génériques sont pris en charge à l'aide de directives IL propriétaires. Le véritable travail d'instanciation générique s'effectue « à la demande » lors de la compilation JIT.
Regardez les métadonnées de la fonction Main dans le code tout à l'heure
.method private hidebysig static void Main(string[] args) cil géré
{
.entrypoint
// Taille du code 79 (0x4f)
.maxstack 2
.locals init ([0] int32 obj,
[1] class CSharpStudy1.Test`1
1)
IL_0000 : nop
IL_0001 : ldc.i4.2
IL_0002 : stloc.0
IL_0003 : ldloc.0
IL_0004 : classe vide d'instance newobj CSharpStudy1.Test`1
IL_0009 : stloc.1
IL_000a : "ldstr "int:"
IL_000f : ldloc.1
IL_0010 : ldfld !0 classe CSharpStudy1. Test`1
IL_0015 : boîte [mscorlib]System.Int3 2
IL_001a : chaîne d'appel [mscorlib]System.String::Concat(object,
🎜> IL_001f : appel void [mscorlib]System.Console::WriteLine(string)
IL_0024 : nop
IL_0025 : ldstr "bonjour tout le monde"
IL_002a : stloc.2
IL_002b : ldloc.2
IL_002c : classe vide d'instance newobj CSharpStudy1.Test`1
IL_0031 : stloc.3
IL_0032 : ldstr "String :"
IL_0037 : ldloc.3
IL_0038 : ldfld !0 classe CSharpStudy1.Test`1& lt;string> : :obj
IL_003d : chaîne d'appel [mscorlib]System.String::Concat(string,
String)
IL_0042 : appel void [mscorlib]System.Console : : WriteLine(string)
IL_0047 : nop
IL_0048 : appel int32 [mscorlib]System.Console::Read()
IL_004d : pop
IL_004 e : ret
} // fin de la méthode Program::Main
Jetons un œil aux métadonnées du constructeur dans la classe Test
.method public hidebysig specialname rtspecialname
instance void .ctor(!T obj) cil géré
{
// Taille du code 17 (0x11)
.maxstack 8
IL_0000 : ldarg.0
IL_0001 : instance d'appel void [mscorlib]System.Object::.ctor()
IL_0006 : nop
IL_0007 : nop
IL_0008 : ldarg.0
IL_0009 : ldarg.1
IL_000a : stfld !0 classe ConsoleCSharpTest1.Test`1 ::obj
IL_000f : nop
IL_0010 : ret
} // fin de la méthode Test`1::.ctor
1. Lors d'un cycle de compilation, le compilateur génère uniquement la "version générique" du code IL et des métadonnées pour le type Test
affiché dans les métadonnées du type Test 2. Lors de la compilation JIT, lorsque le compilateur JIT rencontre Test
affiché dans la fonction Main. 3. CLR génère le même code pour tous les types génériques dont les paramètres de type sont des "types de référence" mais si les paramètres de type sont des "types de valeur", pour chacun Un "type de valeur" différent pour lequel le CLR générera un code distinct. Car lors de l'instanciation d'un type référence générique, la taille allouée en mémoire est la même, mais lors de l'instanciation d'un type valeur, la taille allouée en mémoire est différente.
Fonctionnalités génériques C# :
1. Si les paramètres du type générique instancié sont les mêmes, l'éditeur JIT réutilisera le type, donc les génériques dynamiques de C#. Les capacités de saisie évitent les problèmes de surcharge de code que les modèles statiques C peuvent provoquer.
2. Les types génériques C# contiennent des métadonnées riches, de sorte que les types génériques C# peuvent être appliqués à une technologie de réflexion puissante.
3. Les génériques de C# utilisent la méthode de contrainte de « classe de base, interface, constructeur, type valeur/type référence » pour implémenter des « contraintes explicites » sur les paramètres de type, ce qui améliore non seulement la sécurité du type, mais également la perte de la grande flexibilité des modèles C basés sur des contraintes implicites de "signature"
Héritage générique C# :
C# En plus de pouvoir déclarer les types génériques séparément (En plus de classes et structures), vous pouvez également inclure des déclarations de types génériques dans les classes de base. Mais si la classe de base est une classe générique, son type est soit instancié, soit dérivé des paramètres de type déclarés par la sous-classe (également un type générique
classe C). 🎜>
classe D:C
classe E:C
classe F:C< ; string,int>
class G:C //Illegal
Le type E fournit U et V pour le type C, qui est dérivé de la sous-classe mentionnée ci-dessus
Le type F hérite de C
Le type G est illégal car le type G n'est pas générique et C. est générique, G ne peut pas fournir d'instanciation générique à C
Membres d'un type générique :
Les membres d'un type générique peuvent utiliser des paramètres de type dans la déclaration de type générique. Mais si le paramètre type n'a aucune contrainte, vous ne pouvez utiliser que les membres publics hérités de System.Object sur le type. Comme indiqué ci-dessous :
Interface générique :
Les paramètres de type de l'interface générique sont soit instanciés, soit dérivés des paramètres de type déclarés par la classe d'implémentation
Délégué générique :
Le délégué générique prend en charge l'application de types de paramètres sur les valeurs de retour et les paramètres du délégué. Ces types de paramètres peuvent également être accompagnés de contraintes légales
<.> délégué bool MyDelegate
class MyClass
{
static bool F(int i){...}
static bool G(string s){...}
static void Main()
{
MyDelegate
}
}
Méthode générique :
1. Le mécanisme générique C# ne prend en charge que "contenant des paramètres de type dans la déclaration de méthode", c'est-à-dire les méthodes génériques.
2. Le mécanisme générique C# ne prend pas en charge les paramètres de type dans la déclaration des autres membres (y compris les propriétés, les événements, les indexeurs, les constructeurs et les destructeurs) à l'exception des méthodes, mais ces membres eux-mêmes peuvent contenir dans un type générique et utilisez les paramètres de type du type générique.
3. Les méthodes génériques peuvent être incluses à la fois dans les types génériques et dans les types non génériques.
Déclaration de méthode générique : comme suit
public static int FunctionName
Surcharge des méthodes génériques :
public void Function1
public void Function1(U a);
De cette façon ne peut constituer une surcharge d’une méthode générique. Étant donné que le compilateur ne peut pas déterminer si les types génériques T et U sont différents, il ne peut pas déterminer si les deux méthodes sont différentes
public void Function1
public void Function1(int x);
Cela peut constituer une surcharge
public void Function1
public void Function1
Cela ne peut pas constituer une surcharge d'une méthode générique. Parce que le compilateur ne peut pas déterminer si A et B dans les contraintes sont différents, il ne peut pas déterminer si les deux méthodes sont différentes
Réécriture de méthode générique :
dans Pendant la réécriture processus, les contraintes des méthodes abstraites dans les classes abstraites sont héritées par défaut. Comme suit :
classe abstraite Base
{
résumé public T F
Résumé public T G
}
class MyClass:Base
{
public override
>
Pour les deux méthodes substituées dans MyClass, la méthode
F est légale et les contraintes sont héritées par défaut
. La méthode G est illégale, spécifiée Toutes les contraintes sont redondantes
Contraintes génériques :
1 Les génériques C# nécessitent des contraintes sur les "paramètres de type de tous les types génériques ou méthodes génériques". Les hypothèses sont basées sur des « contraintes explicites » pour maintenir la sécurité de type requise par C#.
2. Les "contraintes explicites" sont exprimées par des clauses Where, et vous pouvez spécifier quatre types de contraintes : "contraintes de classe de base", "contraintes d'interface", "contraintes de constructeur" et "type de valeur/type de référence". contraintes" .
3. Les "contraintes explicites" ne sont pas obligatoires. Si les "contraintes explicites" ne sont pas spécifiées, les paramètres de type générique ne pourront accéder qu'aux méthodes publiques du type System.Object. Par exemple : Dans l'exemple initial, la variable membre obj est définie. Par exemple, nous ajoutons une classe Test1 à l'exemple initial et y définissons deux méthodes publiques Func1 et Func2, comme indiqué ci-dessous :
Ci-dessous commencez à analyser ces contraintes :
Contraintes de classe de base :
class A
{
public void Func1()
{ }
>
classe B
{
public void Func2()
{ }
}
classe C
où S : A
où T : B
{
public C(S s,T t)
{
//Les variables de S peuvent appeler la méthode Func1
s.Func1(>
}
}
Contrainte d'interface :
interface IA< ;T>
{
T Func1();
}
interface IB
{
void Func2();
}
interface IC
{
classe MyClass< T, V>
où T : IA
où V : IB, IC
{
public MyClass (T, v)
{
// T peut appeler Func1
T.Func1 (
// v Les objets peuvent appeler Func2 et Func3
v.Func2(); 🎜> Contrainte de constructeur :
classe A
🎜>
classe B
🎜>
classe C
{
T t;
public C()
{
t = nouveau T();
}
}
classe D
{
public void Func()
C d = nouveau C
}
}
d L'objet obtient une erreur lors de la compilation : Le type B doit avoir un constructeur public sans paramètre afin de l'utiliser comme paramètre 'T' dans le type ou la méthode générique C
Remarque : C# ne prend désormais en charge que non- Contraintes du constructeur de paramètres
À l'heure actuelle, puisque nous avons écrit un constructeur paramétré pour le type B, cela empêche le système de créer automatiquement un constructeur sans paramètre pour B. Cependant, si nous ajoutons un constructeur sans paramètre au type B, l'instanciation de l'objet d ne signalera pas d'erreur. Le type B est défini comme suit :
classe B
{
public B()
{ }
public B(int i)
{ }
}
Type de valeur/type de référence :
public struct A { }
public class B { }
public class C
C c2 = new C();
L'objet c2 a reçu une erreur lors de la compilation : Le type 'B' doit être un type de valeur non nullable afin de utilisez-le comme paramètre 'T' dans le type générique ou la méthode 'C
Résumé :
1 Les capacités génériques de C# sont prises en charge par le CLR à l'adresse suivante. au moment de l'exécution. Il est différent des modèles statiques pris en charge par C au moment de la compilation, et également différent des simples génériques pris en charge par Java utilisant la "méthode d'effacement" au niveau du compilateur.
2. La prise en charge générique de C# comprend quatre types génériques : classes, structures, interfaces, délégués et membres de méthode.
3. Les génériques de C# utilisent la méthode de contrainte de « classe de base, interface, constructeur, type de valeur/type de référence » pour implémenter des « contraintes explicites » sur les paramètres de type. Ils ne prennent pas en charge les modèles implicites basés sur les signatures. contraintes.
Ce qui précède est le contenu de la programmation générique C#. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois (www.php.cn) !