2010-07-21 17 views
15

Ho eseguito il seguente codice:C# Il tipo generico è in scatola?

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

Sto avendo due variabili locali nella funzione principale sono nome e l'età. Li sto stampando usando il metodo console.writeline. Stampa senza problemi. La IL del metodo principale è come illustrato di seguito:

.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 

Ho un'altra classe generica 'GClass'. Nella classe generica ho due proprietà e un metodo (Display). Nel metodo Display, visualizzo le due proprietà nello stesso modo in cui ho visualizzato le variabili locali nel metodo Main. La IL del metodo di visualizzazione Classe Generico è il seguente:

.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 

sto passando 'stringa' come parametro di tipo T1 e l'utilizzo di questo tipo di dichiarare proprietà Name. Quando la proprietà Name viene visualizzata utilizzando Console.Writeline, viene inserito il nome (IL_000c: box! T1). Puoi trovarlo in IL.

Perché la boxe accade nonostante sapessero che è un tipo stringa?

+1

Questo è un sacco di codice da avere in un solo post. Dovresti considerare di ridurre i frammenti di IL alle linee che mostrano la tua domanda. –

+0

Controlla anche questo: http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers –

risposta

6

Questo è così perché compilatore non è sicuro che sia T1 e T2 sarebbe sempre il tipo di riferimento o value-type. quindi li mette in Object di default per entrambi i casi quando T1 o T2, uno di essi è un tipo di valore o un tipo di riferimento.

Il tipo Object può agire in dualità. Può box-Unbox per il valore-tipo e tenere i riferimenti alle istanze di qualsiasi sottoclasse tipi quando è un riferimento di tipo.

Quindi nel caso in cui T1 sia una stringa, in realtà non è un parallelepipedo, contiene il riferimento dell'istanza di stringa perché Object è la classe base del tipo di stringa, in pratica qualsiasi tipo .Net.

e nel caso in cui T2 è int, è semplice boxe-unboxing.

+0

Ciao, Grazie per l'asnwer. Penso che sia ancora una scatola, ma quando cerca di boxare trova il tipo come tipo di riferimento, quindi non fa nulla come menzionato da Thomas Levesque. – RAM

6

Il compilatore deve generare IL che può funzionare in tutti i tipi generici. Il compilatore non può sapere che instatyte sempre GCClass con <string, int>. Deve far fronte all'eventualità che T1 sia un tipo di valore.

Tuttavia, mi aspetto box su un tipo di riferimento per essere un no-op. Il JIT genera un codice macchina diverso dall'IL Display per i tipi di riferimento e valore. Per i tipi di riferimento, mi aspetto che l'istruzione box sia eliminata.

Se sei sicuro che T1 sarà mai un tipo di valore è possibile aggiungere un vincolo : class ad esso, che rimuoverà che box istruzioni.

+0

Tim, questo ha senso. Grazie per la risposta. – RAM

4

Scopri la CLI specification

In partizione III, sezione 4.1, circa l'istruzione box:

Se typeTok è un tipo di valore, l'istruzione casella converte val sua scatola forma. Quando typeTok è un tipo non annullabile (§1.8.2.4), questo viene fatto da creando un nuovo oggetto e copiando i dati da val nell'oggetto appena assegnato. Se è un tipo nullable, questo viene eseguito esaminando la proprietà Hasvalue di val; se è falso, un riferimento nullo viene inserito nello stack; in caso contrario, il risultato della proprietà Valore di boxing val viene inserito nello stack . Se typeTok è un tipo di riferimento , l'istruzione di dialogo non fa nulla

Così la boxe si verifica solo se il parametro di tipo generico è in realtà un tipo di valore. Se si tratta di un tipo di riferimento, questa istruzione non ha alcun effetto.

+0

Ciao Thomas, Grazie per la risposta. Credo che durante la JIT decida se boxare o meno in base al tipo effettivo dell'operando. Ho ragione? – RAM

+0

@ RAM sì, vedere la mia risposta –

Problemi correlati