2009-01-12 33 views
24

我剛剛參與堆棧溢出問題Is everything in .NET an object?在.NET中調用一個值類型的方法會導致裝箱嗎?

和一個海報(在接受的答案的評論中)似乎認爲執行方法調用值類型導致拳擊。他指着我Boxing and Unboxing (C# Programming Guide)這並不完全指定我們描述的用例。

我不是一個信任單一來源的人,所以我只想獲得關於這個問題的進一步反饋。我的直覺是沒有拳擊,但我的直覺確實很糟糕。 :d

爲了進一步闡述:

我使用的例子是:

int x = 5; 
string s = x.ToString(); // Boxing?? 

拳擊確實發生,如果有問題的結構將覆蓋從對象繼承在這裏接受的答案的方法狀態。

但是,如果結構不覆蓋該方法,則在callvirt之前執行「約束」CIL命令。根據該文件,OpCodes.Constrained Field這導致在拳擊

如果thisType是值類型和 thisType沒有實現方法 然後PTR解除引用,裝箱,並 爲「這傳遞'指向callvirt方法指令的指針 。

+0

這裏的原因:http://stackoverflow.com/questions/1359856/why-does-implicitly-calling-tostring-on-a-value-type-cause-a-box-instruction – nawfal 2013-06-10 09:54:27

回答

17

這裏的IL爲您的代碼:

L_0001: ldc.i4.5  // get a 5 on the stack 
L_0002: stloc.0  // store into x 
L_0003: ldloca.s x // get the address of x on the stack 
L_0005: call instance string [mscorlib]System.Int32::ToString() // ToString 
L_000a: stloc.1  // store in s 

因此,在這種情況下,答案是否定的。

+1

重要請注意,ToString方法不是針對x的值而是針對x的地址進行調用的。注意ldloc ** a **指令。 – swax 2012-10-01 21:00:32

11

在這種情況下,你給出的答案是否定的,正如底座所指出的那樣。

但是,它會通過接口指針調用方法。

考慮代碼:

interface IZot 
{ 
    int F(); 
} 

struct Zot : IZot 
{ 
    public int F() 
    { 
     return 123; 
    } 
} 

然後

Zot z = new Zot(); 
z.F(); 

是否結果在拳擊:

.locals init (
    [0] valuetype ConsoleApplication1.Zot z) 
L_0000: nop 
L_0001: ldloca.s z 
L_0003: initobj ConsoleApplication1.Zot 
L_0009: ldloca.s z 
L_000b: call instance int32 ConsoleApplication1.Zot::F() 
L_0010: pop 
L_0011: ret 

然而,這並不:

IZot z = new Zot(); 
z.F(); 

    .locals init (
     [0] class ConsoleApplication1.IZot z, 
     [1] valuetype ConsoleApplication1.Zot CS$0$0000) 
    L_0000: nop 
    L_0001: ldloca.s CS$0$0000 
    L_0003: initobj ConsoleApplication1.Zot 
    L_0009: ldloc.1 
    L_000a: box ConsoleApplication1.Zot 
    L_000f: stloc.0 
    L_0010: ldloc.0 
    L_0011: callvirt instance int32 ConsoleApplication1.IZot::F() 
    L_0016: pop 
4

我相信如果結構不覆蓋方法,調用ToString,Equals和Gethashcode會導致裝箱。

+0

簡單,直截了當。應該是正確的答案 – nawfal 2013-05-14 04:37:55

7

@ggf31316

「我相信調用toString, 的Equals和GetHashCode結果 拳擊,如果結構不 覆蓋過的方法。」

我檢查過ToString。 Int32覆蓋了ToString,所以我做了一個沒有的結構。我使用.NET Reflector來確保結構不會以某種方式神奇地覆蓋ToString(),而不是。 ,

IL_0000: ldloca.s ms 
    IL_0002: ldc.i4.5 
    IL_0003: call  instance void ConsoleApplication29.MyStruct::.ctor(int32) 
    IL_0008: ldloca.s ms 
    IL_000a: constrained. ConsoleApplication29.MyStruct 
    IL_0010: callvirt instance string [mscorlib]System.Object::ToString() 
    IL_0015: stloc.1 
    IL_0016: ldloc.1 
    IL_0017: call  void [mscorlib]System.Console::WriteLine(string) 
    IL_001c: ret 

現在雖然沒有拳擊調用發生,如果您:

因此,代碼是這樣的:

using System; 

namespace ConsoleApplication29 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyStruct ms = new MyStruct(5); 
      string s = ms.ToString(); 
      Console.WriteLine(s); 
     } 
    } 

    struct MyStruct 
    { 
     private int m_SomeInt; 

     public MyStruct(int someInt) 
     { 
      m_SomeInt = someInt; 
     } 

     public int SomeInt 
     { 
      get 
      { 
       return m_SomeInt; 
      } 
     } 
    } 
} 

而且MSIL(通過ILDASM)爲主要方法是這樣的檢查the documentation about a constrained + a call virt,你會發現它表明,拳擊是否發生。 OOO

引用:

如果thisType是值類型和 thisType不實現方法 然後PTR被廢棄時,盒裝,和 作爲「這個」指針 callvirt方法指令通過。

相關問題