Question

In the example below, I call a virtual method on a value type (int):

namespace ShortTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 42;
            i.ToString();
            ((object)i).ToString();
        }
    }
}

Looking at the generated CIL in Reflector, I can see the following code for the two calls:

.locals init ([0] int32 i, ...)
...
L_001a: ldloca.s i
L_001c: call instance string [mscorlib]System.Int32::ToString()
L_0021: pop
L_0022: ldloc.0
L_0023: box int32
L_0028: callvirt instance string [mscorlib]System.Object::ToString()
L_002d: pop
...

The this parameter is a managed pointer to the int in the first case, and a reference to a boxed int (i.e. a pointer to an object with a header field and an int field) in the second case.

Since the same method is used for both calls (int implements ToString()), how can this work ? The System.Int32::ToString() method uses ldind.i4 on the this pointer to retrieve the int's value, so it should get the int's value in the first case, but it should get the value of the boxed int's first field (the header) in the second case:

L_0000: ldarg.0
L_0001: ldind.i4
...
Was it helpful?

Solution

but it should get the value of the boxed int's first field (the header) in the second case

You're assuming the ToString method is passed a pointer, and whatever bytes happen to be pointed to are what gets read. That assumption is wrong.

An instance method of a value type behaves like it has a ref T "this" parameter. The .NET runtime will make sure this gets passed correctly regardless of whether the value type is boxed, so that the method does not need to give boxed values any special treatment.

Your starting point is correct:

I.8.9.7 Value type definition

When a non-static method (i.e., an instance or virtual method) is called on the value type, its this pointer is a managed reference to the instance, whereas when the method is called on the associated boxed type, the this pointer is an object reference.

However, this happens in such a way that the same ldind.i4 instruction works regardless. Only when it refers to an unmanaged pointer will it simply read bytes.

You found

II.13.3 Methods of value types

Instance and virtual methods of classes shall be coded to expect a reference to an instance of the class as the this pointer. By contrast, instance and virtual methods of value types shall be coded to expect a managed pointer (see Partition I) to an unboxed instance of the value type. The CLI shall convert a boxed value type into a managed pointer to the unboxed value type when a boxed value type is passed as the this pointer to a virtual method whose implementation is provided by the unboxed value type.

which at first glance contradicts the above, but actually says the implementation must simply use whatever magic is necessary to make it work.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top