2009-01-12 85 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方法指令通过。

相关问题