2010-07-21 74 views
15

我執行以下代碼:C#泛型類型是盒裝?

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

我有在主函數兩個局部變量他們的姓名和年齡。我使用console.writeline方法打印它們。它打印沒有任何問題。主要方法的IL如下所示:

.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 

我有另一個泛型類'GClass'。在泛型類中,我有兩個屬性和一個方法(Display)。在Display方法中,我使用與Main方法中顯示局部變量相同的方式顯示兩個屬性。通用類顯示方法的IL下面給出:

.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 

我傳遞「字符串」作爲類型參數T1和使用這種類型的聲明名稱屬性。當使用Console.Writeline顯示名稱屬性時,它將裝箱名稱(IL_000c:box!T1)。你可以在IL中找到它。

爲什麼拳擊發生,儘管它是一個字符串類型?

+1

這是一個帖子中有很多代碼。您應該考慮將IL片段縮小到顯示您問題的線條。 – 2010-07-21 10:05:28

+0

也檢查此:http://stackoverflow.com/questions/32664/c-generic-constraint-for-only-integers – 2010-07-21 10:17:32

回答

6

這是如此,因爲編譯器不確定T1T2將始終是引用類型或值類型。因此在T1或T2時,它們都是默認值,這兩種情況都是值類型或引用類型。

該類型Object可以在雙重性行事。它可以是對於值類型的box-unbox在引用類型時保留對任何子類類型的實例的引用。

所以在T1是字符串的情況下,它實際上不是裝箱,它保存字符串實例的引用,因爲Object是字符串類型的基類,實際上是任何.Net類型。

並且在T2是int的情況下,它是簡單的裝箱 - 拆箱。

+0

嗨, 感謝您的幫助。我認爲,它仍然是一個盒子,但是當它試圖裝箱時,它發現類型是參考類型,因此沒有托馬斯·列維斯克所說的那樣。 – RAM 2010-07-21 10:32:48

6

編譯器必須生成可跨所有通用類型工作的IL。編譯器無法知道你總是用<string, int>安裝GCClass。它必須應對T1是一種價值型的可能性。

但是,我認爲box在參考類型是一個無操作。 JIT從Display方法的IL中爲參考和值類型生成不同的機器代碼。對於參考類型,我期望box指令被消除。

如果您確定T1永遠不會是值類型,您可以添加一個: class約束條件,它將刪除box指令。

+0

蒂姆,這是有道理的。感謝你的回答。 – RAM 2010-07-21 10:25:17

4

檢查出CLI specification

在分區III,第4.1節,關於box指令:

如果typeTok爲值類型,盒 指令轉換VAL其盒裝 形式。當typeTok是不可空的 類型(§1.8.2。4),這是通過 創建新對象並將來自val的 數據複製到新分配的 對象中完成的。如果它是可空類型,則通過檢查val的HasValue 屬性完成此 ;如果它是假的,則將一個空的 引用推入堆棧; 否則,裝箱val的結果 Value屬性被壓入 堆棧。 如果typeTok是引用 型,箱指令不

所以拳擊發生只有在泛型類型參數實際上是一個值類型。如果它是參考類型,則該指令不起作用。

+0

Hi Thomas, 感謝您的回答。我相信在JIT期間,它會根據操作數的實際類型決定是否進行裝箱。我對嗎? – RAM 2010-07-21 10:34:16

+0

@RAM是的,看到我的答案 – 2010-07-21 10:37:06