2010-07-21 19 views
15

ich ausgeführt, um den folgenden Code ein:C# Generischer Typ ist eingerahmt?

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(); 
     } 
    } 
} 

ich in der Funktion Main zwei lokale Variablen habe sie sind Namen und das Alter. Ich drucke sie mit der console.writeline-Methode. Es druckt ohne Probleme. Die IL des Hauptverfahrens wird, wie unten gezeigt:

.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 

I eine andere generische Klasse ‚GClass‘ haben. In der generischen Klasse habe ich zwei Eigenschaften und eine Methode (Display). In der Anzeige-Methode zeige ich die beiden Eigenschaften genauso an wie die lokalen Variablen in der Main-Methode. Die IL der allgemeinen Klasse Anzeigemethode ist unten angegeben:

.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 

ich diese Art Eigenschaft Name erklären ‚string‘ als Typ-Parameter auf T1 und bin mit geben. Wenn die Name-Eigenschaft mit Console.Writeline angezeigt wird, wird der Name eingepackt (IL_000c: box! T1). Sie können dies in IL finden.

Warum Boxen passiert, obwohl es ein String-Typ ist?

+1

Das ist eine Menge Code in einem Post zu haben. Sie sollten in Betracht ziehen, die IL-Snippets auf die Zeilen zu reduzieren, die Ihre Frage veranschaulichen. –

+0

Überprüfen Sie auch dies: http://StackOverflow.com/Questions/32664/c-generic-constraint-for-only-integers –

Antwort

6

Dies ist so, weil Compiler nicht sicher ist, dass sowohl T1 und T2 immer ein Referenztyp oder Wert-Typ sein würden. Sie werden also standardmäßig für beide Fälle in Object eingefügt, wenn T1 oder T2 eine der beiden Typen ist. Der Typ Object kann in der Dualität wirken. Es kann Box-Unbox für Wert-Typen und Referenzen auf die Instanzen der Unterklasse Typen enthalten, wenn es ein Referenz-Typ ist.

Also wenn T1 Zeichenfolge ist, ist es tatsächlich nicht Boxen, es hält den Verweis der Zeichenfolge-Instanz, weil Objekt die Basisklasse des Zeichenfolge-Typs ist, in der Tat alle. NET-Typ.

und wenn T2 int ist, ist es einfach Boxen-Unboxing.

+0

Hallo, Danke für die asnwer. Ich denke, es ist immer noch eine Box, aber wenn es versucht zu boxen, finde ich den Typ als Referenztyp, also tut nichts, was Thomas Levesque erwähnt. – RAM

6

Der Compiler muss IL generieren, die über alle generischen Typen hinweg funktionieren kann. Der Compiler kann nicht wissen, dass Sie immer GCClass mit <string, int> instatieren. Es muss mit der Möglichkeit umgehen, dass T1 ein Werttyp ist.

Allerdings würde ich erwarten box auf einen Referenztyp zu einem No-Op. Das JIT generiert aus der IL der Methodefür Referenz- und Werttypen einen anderen Maschinencode. Für Referenztypen würde ich erwarten, dass die Anweisung box eliminiert wird.

Wenn Sie sicher sind, dass T1 niemals ein Werttyp ist, können Sie eine : class-Einschränkung hinzufügen, die diese box Anweisung entfernt.

+0

Tim, das macht Sinn. Danke für die Antwort. – RAM

4

Schauen Sie sich die CLI specification

In Partition III, Abschnitt 4.1, über die box Anweisung:

Wenn typeTok ein Werttyp ist, die Box Befehl konvertiert val seiner boxed Form. Wenn es sich bei typeTok um einen nicht nullbaren Typ handelt (§1.8.2.4), geschieht dies durch Erstellen eines neuen Objekts und Kopieren der Daten von Wert in das neu zugeordnete Objekt . Wenn es sich um einen Nullwerttyp handelt, wird durch Überprüfung der Eigenschaft HasValue von val ausgeführt. wenn es falsch ist, wird eine Null Referenz auf den Stapel geschoben; andernfalls wird das Ergebnis der Value-Eigenschaft boxing vals auf den -Stack geschoben. Wenn typeTok eine Referenz Typ ist, hat der Box-Befehl nichts

So die Boxen treten nur, wenn der generische Typparameter tatsächlich ein Werttyp ist. Wenn es sich um einen Referenztyp handelt, hat diese Anweisung keine Wirkung.

+0

Hallo Thomas, Danke für die Antwort. Ich glaube, dass es während JIT entscheidet, basierend auf dem tatsächlichen Typ des Operanden zu boxen oder nicht. Habe ich recht? – RAM

+0

@RAM Ja, siehe meine Antwort –