Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用户定义值类型的装箱_C#_.net_Il_Value Type - Fatal编程技术网

C# 用户定义值类型的装箱

C# 用户定义值类型的装箱,c#,.net,il,value-type,C#,.net,Il,Value Type,根据MSDN,如果定义了一个结构,该结构应该覆盖从对象类继承的所有方法。建议这样做,以避免在调用任何继承方法(如ToString)时出现不必要的装箱 根据MSDN,为了确定是否以及何时发生装箱,可以在MSIL代码中找到IL指令“box” 我写了下面的测试来看看拳击 using System; namespace TestingBoxing { public struct StructX { public int member1; public i

根据MSDN,如果定义了一个结构,该结构应该覆盖从对象类继承的所有方法。建议这样做,以避免在调用任何继承方法(如ToString)时出现不必要的装箱

根据MSDN,为了确定是否以及何时发生装箱,可以在MSIL代码中找到IL指令“box”

我写了下面的测试来看看拳击

using System;

namespace TestingBoxing
{
    public struct StructX
    {
        public int member1;
        public int member2;
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            StructX s1;

            s1.member1 = 2;
            s1.member2 = 5;

            string str = s1.ToString();

            Console.WriteLine(str);
        }
    }
}
但是,在下面的MSIL代码中无法看到boxing指令,尽管在结构定义中未重写的情况下调用了ToString

.method public hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       37 (0x25)
  .maxstack  2
  .locals init ([0] valuetype TestingBoxing.StructX s1,
           [1] string str)
  IL_0000:  ldloca.s   s1
  IL_0002:  ldc.i4.2
  IL_0003:  stfld      int32 TestingBoxing.StructX::member1
  IL_0008:  ldloca.s   s1
  IL_000a:  ldc.i4.5
  IL_000b:  stfld      int32 TestingBoxing.StructX::member2
  IL_0010:  ldloca.s   s1
  IL_0012:  constrained. TestingBoxing.StructX
  IL_0018:  callvirt   instance string [mscorlib]System.Object::ToString()
  IL_001d:  stloc.1
  IL_001e:  ldloc.1
  IL_001f:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0024:  ret
} // end of method Program::Main 
这怎么解释呢


参考文章:

在我看来,是callvirt指令完成了装箱。查看代码的反汇编,我们在调用ToString的行中得到了这个反汇编

00DB287A  mov         ecx,26933C0h  
00DB287F  call        00AD2100  
00DB2884  mov         dword ptr [ebp-18h],eax  
00DB2887  mov         edi,dword ptr [ebp-18h]  
00DB288A  add         edi,4  
00DB288D  lea         esi,[ebp-10h]  
00DB2890  movq        xmm0,mmword ptr [esi]  
00DB2894  movq        mmword ptr [edi],xmm0  
00DB2898  mov         ecx,dword ptr [ebp-18h]  
00DB289B  mov         eax,dword ptr [ecx]  
00DB289D  mov         eax,dword ptr [eax+28h]  
00DB28A0  call        dword ptr [eax]  
00DB28A2  mov         dword ptr [ebp-1Ch],eax  
00DB28A5  mov         eax,dword ptr [ebp-1Ch]  
00DB28A8  mov         dword ptr [ebp-14h],eax 
如果我们将代码更改为:

public struct StructX
{
    public int member1;
    public int member2;

    public override string ToString()
    {
        return member1.ToString() + " " + member2.ToString();
    }
}
我们得到:

02352875  lea         ecx,[ebp-8]  
02352878  call        dword ptr ds:[4DD33E0h]  
0235287E  mov         dword ptr [ebp-10h],eax  
02352881  mov         eax,dword ptr [ebp-10h]  
02352884  mov         dword ptr [ebp-0Ch],eax  
现在我的组件已经生锈了,但在我看来,所有这些移动实际上是拳击。当类型是值类型时,C#编译器可以跳过调用virtual,因为可以确定该方法不能在派生类型中重写


编辑:正如其他答案所指出的,callvirt仍然存在,是CLR进行了优化。

这可以通过查看
约束
的功能来解释

字段通常受
约束
,以便以标准方式使用
callvirt
,而无需显式地选择框。它做了以下工作:

如果thisType是引用类型(与值类型相反),那么ptr将被取消引用并作为指向callvirt of方法的“this”指针传递

如果thisType是值类型,并且thisType实现方法,那么对于thisType实现方法,ptr将作为指向调用方法指令的“this”指针进行传递,而不作修改

如果thisType是值类型,并且thisType未实现方法,则ptr将被取消引用、装箱并作为指向callvirt方法指令的“this”指针传递

这意味着(如MSDN文章所述):

只有在对象、ValueType或枚举上定义了方法且未被此类型重写时,才会出现最后一种情况。在这种情况下,装箱会导致生成原始对象的副本但是,由于Object、ValueType和Enum的任何方法都不会修改对象的状态,因此无法检测到这一事实。

我的。基本上说,如果拳击真的发生,它不能通过IL来确定


Constrained MSDN:

也许它隐含在
Constrained
调用中。请参见此处:.constrained在此上下文中似乎没有效果,我尝试重写ToString方法,但生成了.constrained。如果我理解正确,将始终生成constrained,只是它的作用会发生更改