2010-07-21 7 views
15

J'ai exécuté le code suivant:C# Type générique est encadré?

using System; 
using System.Collections.Generic; 

namespace TestReleaseAndDebug 
{ 
    public class GClass<T1, T2> 
    { 
     public T1 Name { get; set; }  
     public T2 Age { get; set; } 

     public void Display() 
     { 
      Console.WriteLine("Name: " + Name);   
      Console.WriteLine("Age: " + Age); 
     } 
    } 

    class Program 
    {   
     static void Main(string[] args) 
     { 
      GClass<string, int> person = new GClass<string, int>(); 
      person.Name = "RAM";   
      person.Age = 34; 
      string name = "RAM";   
      int age = 34; 

      Console.WriteLine("Name: " + name);   
      Console.WriteLine("Age: " + age);   
      person.Display(); 

      Console.Read(); 
     } 
    } 
} 

J'ai deux variables locales dans la fonction principale, ils sont le nom et l'âge. Je les imprime en utilisant la méthode console.writeline. Il imprime sans problème. Le IL de la méthode principale est comme indiqué ci-dessous:

.method private hidebysig static void Main(string[] args) cil managed 
{ 
    .entrypoint 
    // Code size  90 (0x5a) 
    .maxstack 2 
    .locals init ([0] class TestReleaseAndDebug.GClass`2<string,int32> person, 
      [1] string name, 
      [2] int32 age) 
    IL_0000: nop 
    IL_0001: newobj  instance void class TestReleaseAndDebug.GClass`2<string,int32>::.ctor() 
    IL_0006: stloc.0 
    IL_0007: ldloc.0 
    IL_0008: ldstr  "RAM" 
    IL_000d: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Name(!0) 
    IL_0012: nop 
    IL_0013: ldloc.0 
    IL_0014: ldc.i4.s 34 
    IL_0016: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::set_Age(!1) 
    IL_001b: nop 
    IL_001c: ldstr  "RAM" 
    IL_0021: stloc.1 
    IL_0022: ldc.i4.s 34 
    IL_0024: stloc.2 
    IL_0025: ldstr  "Name: " 
    IL_002a: ldloc.1 
    IL_002b: call  string [mscorlib]System.String::Concat(string, 
                   string) 
    IL_0030: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_0035: nop 
    IL_0036: ldstr  "Age: " 
    IL_003b: ldloc.2 
    IL_003c: box  [mscorlib]System.Int32 
    IL_0041: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0046: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_004b: nop 
    IL_004c: ldloc.0 
    IL_004d: callvirt instance void class TestReleaseAndDebug.GClass`2<string,int32>::Display() 
    IL_0052: nop 
    IL_0053: call  int32 [mscorlib]System.Console::Read() 
    IL_0058: pop 
    IL_0059: ret 
} // end of method Program::Main 

J'ai une autre classe générique 'GClass'. Dans la classe générique, j'ai deux propriétés et une méthode (affichage). Dans la méthode Display, j'affiche les deux propriétés de la même manière que j'ai affiché les variables locales dans la méthode Main. L'IL de la méthode générique de classe d'affichage est donnée ci-dessous:

.method public hidebysig instance void Display() cil managed 
{ 
    // Code size  56 (0x38) 
    .maxstack 8 
    IL_0000: nop 
    IL_0001: ldstr  "Name: " 
    IL_0006: ldarg.0 
    IL_0007: call  instance !0 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Name() 
    IL_000c: box  !T1 
    IL_0011: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0016: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_001b: nop 
    IL_001c: ldstr  "Age: " 
    IL_0021: ldarg.0 
    IL_0022: call  instance !1 class TestReleaseAndDebug.GClass`2<!T1,!T2>::get_Age() 
    IL_0027: box  !T2 
    IL_002c: call  string [mscorlib]System.String::Concat(object, 
                   object) 
    IL_0031: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_0036: nop 
    IL_0037: ret 
} // end of method GClass`2::Display 

Je passe « string » comme paramètre de type T1 et en utilisant ce type de déclarer propriété Name. Lorsque la propriété Name est affichée à l'aide de Console.Writeline, elle est encadrée par le nom (IL_000c: boîte! T1). Vous pouvez le trouver dans IL.

Pourquoi la boxe se produit même si c'est un type de chaîne?

+1

C'est beaucoup de code à avoir dans un article. Vous devriez envisager de réduire les extraits IL aux lignes qui illustrent votre question. –

+0

Vérifiez également ceci: http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers –

Répondre

6

Il en est ainsi parce que compilateur n'est pas sûr que les deux T1 et T2 serait toujours un type de référence ou valeur de type. donc il les met dans l'objet par défaut pour les deux cas quand T1 ou T2, l'un ou l'autre est un type de valeur ou un type de référence.

Le type Object peut agir en dualité. Il peut box-unbox pour les types de valeur et contenir des références aux instances de tous les types de sous-classe lorsqu'il s'agit d'un type de référence. Donc, dans le cas où T1 est une chaîne, il ne s'agit pas d'une boîte, mais de la référence de l'occurrence de chaîne car Object est la classe de base du type chaîne, en fait tout type .Net. Et dans le cas où T2 est int, c'est simple boxing-unboxing.

+0

Salut, Merci pour l'asnwer. Je pense que c'est toujours une boîte, mais quand on essaie de la mettre en boîte, on trouve le type comme type de référence, donc on ne fait rien comme Thomas Levesque l'a mentionné. – RAM

6

Le compilateur doit générer IL qui peut fonctionner sur tous les types génériques. Le compilateur ne peut pas savoir que vous installez toujours GCClass avec <string, int>. Il doit faire face à l'éventualité que T1 soit un type de valeur.

Cependant, je m'attendrais à ce que box sur un type de référence soit un no-op. Le JIT génère un code machine différent de celui de la méthode Display pour les types de référence et de valeur. Pour les types de référence, je m'attendrais à ce que l'instruction box soit éliminée.

Si vous êtes sûr que T1 ne sera jamais un type de valeur, vous pouvez lui ajouter une contrainte : class, ce qui supprimera cette instruction box.

+0

Tim, cela a du sens. Merci d'avoir répondu. – RAM

4

Vérifiez la CLI specification

Dans la partition III, section 4.1, à propos de l'instruction box:

Si typeTok est un type de valeur, la boîte instruction convertit val à sa forme boxed. Lorsque typeTok est un type non nul (§1.8.2.4), ceci est effectué par en créant un nouvel objet et en copiant les données de val dans l'objet nouvellement attribué. S'il s'agit d'un type Nullable, ce est effectué en inspectant la propriété HasValue de Val; si elle est fausse, une référence nulle est poussée sur la pile; sinon, le résultat de la propriété Value de boxing val est transmis à la pile . Si typeTok est une référence de type , l'instruction de boîte ne fait rien

Ainsi, la boxe se produit uniquement si le paramètre de type générique est en fait un type de valeur. S'il s'agit d'un type de référence, cette instruction n'a aucun effet.

+0

Salut Thomas, Merci pour la réponse. Je crois que pendant JIT il décide s'il faut boxer ou non en fonction du type réel de l'opérande. Ai-je raison? – RAM

+0

@RAM oui, voir ma réponse –

Questions connexes