Reputation: 1157
According to MSDN, if a struct were defined, that struct should override all methods inherited from the object class. That is recommended to avoid the unnecessary boxing when calling any inherited method such as ToString.
According to the MSDN, to determine if and when boxing occurs, an IL instruction "box" can be found in the MSIL code.
I wrote the following test to see the boxing.
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);
}
}
}
However, the boxing instruction cannot be seen in the MSIL code below although ToString is called without being overridden in the struct definition.
.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
How that could be explained ?
Reference article: http://msdn.microsoft.com/en-us/library/ms973858.aspx#code-snippet-6
Upvotes: 3
Views: 423
Reputation: 23561
In my opinion it is the callvirt instruction that does the boxing. Looking at the disassembly for your code we get this disassembly on the line where ToString is called
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
And if we change the code to:
public struct StructX
{
public int member1;
public int member2;
public override string ToString()
{
return member1.ToString() + " " + member2.ToString();
}
}
We get:
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
Now my assembly is quite rusty but it seems to me that all this moving around is actually boxing. When the type is a value type The C# compiler can skip call virtual since it is certain that the method cannot be overridden in a derived type.
Edit: As pointed by other answers the callvirt is still there it is the CLR that does the optimization.
Upvotes: 1
Reputation: 7121
This can be explained by looking at what Constrained
does.
A field is typically constrained
in order to use callvirt
in a standard way without needing to explicitly box. It does the following:
If thisType is a reference type (as opposed to a value type) then ptr is dereferenced and passed as the 'this' pointer to the callvirt of method.
If thisType is a value type and thisType implements method then ptr is passed unmodified as the 'this' pointer to a call method instruction, for the implementation of method by thisType.
If thisType is a value type and thisType does not implement method then ptr is dereferenced, boxed, and passed as the 'this' pointer to the callvirt method instruction.
What this means is (as stated by the MSDN article):
This last case can occur only when method was defined on Object, ValueType, or Enum and not overridden by thisType. In this case, the boxing causes a copy of the original object to be made. However, because none of the methods of Object, ValueType, and Enum modify the state of the object, this fact cannot be detected.
Emphasis mine. Basically saying that if boxing does occur, it cannot be determined via IL.
Constrained MSDN: http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.constrained%28v=vs.110%29.aspx
Upvotes: 6