2015-01-18 3 views
3

Согласно MSDN, если была определена структура, эта структура должна переопределять все методы, унаследованные от класса объекта. Рекомендуется избегать ненужного бокса при вызове любого унаследованного метода, такого как ToString.Бокс пользовательских типов значений

В соответствии с MSDN, чтобы определить, происходит ли и когда происходит бокс, поле «инструкция» IL-кода можно найти в коде MSIL.

Я написал следующий тест, чтобы увидеть бокс.

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 ниже, хот 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 

Как это можно объяснить?

Ссылка статьи: http://msdn.microsoft.com/en-us/library/ms973858.aspx#code-snippet-6

+1

Возможно, это подразумевается в 'constrained' вызова. См. Здесь: http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained%28v=vs.110%29.aspx –

+0

.constrained, похоже, не имеет никакого эффекта в этом контексте, Я попытался переопределить метод ToString, но был создан .constrained. –

+0

сдержанный всегда будет сгенерирован, если я правильно пойму, только то, что он сделает, изменится –

ответ

5

Это можно объяснить, посмотрев, что делает Constrained.

Поле, как правило, constrained, чтобы использовать callvirt стандартным способом, без необходимости явно указывать. Он выполняет следующие действия:

Если thisType является ссылочным типом (в отличие от типа значения), то ptr разыменовывается и передается как «этот» указатель на callvirt метода.

Если thisType является типом значения, и thisType реализует метод, тогда ptr передается немодифицированным как «этот» указатель на инструкцию метода вызова для реализации метода thisType.

Если thisType является типом значения, и этот тип не реализует метод, тогда ptr разыменовывается, помещается в квадрат и передается как «этот» указатель на инструкцию метода callvirt.

Что это означает (как указано в статье MSDN):

Этот последний случай может иметь место только тогда, когда метод был определен на Object, ValueType или Enum и не переопределены thisType. В этом случае бокс вызывает копирование исходного объекта.Однако, поскольку ни один из методов Object, ValueType и Enum не изменяет состояние объекта, этот факт не может быть обнаружен.

Emphasis mine. В основном говоря, что если бокс действительно происходит, он не может быть определен через ИЛ.

Constrained MSDN: http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained%28v=vs.110%29.aspx

1

На мой взгляд, это инструкция 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, которая делает оптимизацию.