Monday, May 21, 2007

Boxing - value types

I encountered this in Chapter 1 of "MCTS Self-Paced Training Kit (Exam 70-536): Microsoft® .NET Framework 2.0—Application Development Foundation" - MSPress Book!

Question: Structures inherit ToString from System.Object. Why would someone override that method within a structure? (Choose as many correct answers as apply.)
A. To avoid boxing.
B. To return something other than the type name.
C. The compiler requires structures to override the ToString method.
D. To avoid run-time errors caused by invalid string conversions
Correct Answers: A and B
A. Correct: Value types are boxed when an abstract method inherited from System.Object is called. Overriding the method avoids boxing.
B. Correct: By default, the ToString method simply returns the type name, which is typically not useful for a consuming application.
C. Incorrect: The compiler does not require structures to override the ToString method.
D. Incorrect: ToString never causes a run-time error; it simply returns the type name unless overridden.

O'kay I shall provide feedback to author regarding correction.


I have written two simple programs to understand the behavior correctly:
Program 1

public struct MyStruct1
{
public int i;
public override string ToString()
{
return i.ToString();
}
}
public struct MyStruct2
{
public int i;
}

class Program
{
static void Main(string[] args)
{
MyStruct1 st1;
st1.i = 55;
Console.WriteLine(st1.ToString());

MyStruct2 st2;
st2.i = 99;
Console.WriteLine(st2.ToString());
}
}

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 58 (0x3a)
.maxstack 2
.locals init ([0] valuetype BoxUnBox.MyStruct1 st1,
[1] valuetype BoxUnBox.MyStruct2 st2)
IL_0000: nop
IL_0001: ldloca.s st1
IL_0003: ldc.i4.s 55
IL_0005: stfld int32 BoxUnBox.MyStruct1::i
IL_000a: ldloca.s st1
IL_000c: constrained. BoxUnBox.MyStruct1
IL_0012: callvirt instance string [mscorlib]System.Object::ToString()
IL_0017: call void [mscorlib]System.Console::WriteLine(string)
IL_001c: nop
IL_001d: ldloca.s st2
IL_001f: ldc.i4.s 99
IL_0021: stfld int32 BoxUnBox.MyStruct2::i
IL_0026: ldloca.s st2
IL_0028: constrained. BoxUnBox.MyStruct2
IL_002e: callvirt instance string [mscorlib]System.Object::ToString()
IL_0033: call void [mscorlib]System.Console::WriteLine(string)
IL_0038: nop
IL_0039: ret
} // end of method Program::Main


Program 2

... definition of both structs remains the same as above!
[-snip-]
static void Main(string[] args)
{
MyStruct1 st1;
st1.i = 55;
Console.WriteLine(st1);

MyStruct2 st2;
st2.i = 99;
Console.WriteLine(st2);
}

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 44 (0x2c)
.maxstack 2
.locals init ([0] valuetype BoxUnBox.MyStruct1 st1,
[1] valuetype BoxUnBox.MyStruct2 st2)
IL_0000: nop
IL_0001: ldloca.s st1
IL_0003: ldc.i4.s 55
IL_0005: stfld int32 BoxUnBox.MyStruct1::i
IL_000a: ldloc.0
IL_000b: box BoxUnBox.MyStruct1
IL_0010: call void [mscorlib]System.Console::WriteLine(object)
IL_0015: nop
IL_0016: ldloca.s st2
IL_0018: ldc.i4.s 99
IL_001a: stfld int32 BoxUnBox.MyStruct2::i
IL_001f: ldloc.1
IL_0020: box BoxUnBox.MyStruct2
IL_0025: call void [mscorlib]System.Console::WriteLine(object)
IL_002a: nop
IL_002b: ret
} // end of method Program::Main



The above experiment shows that - in both cases MyStruct1 and MyStruct2 are boxed irrespective of ToString() was overridden in MyStruct1.

One question: Why would there be boxing at all in first case (in Program 1 above):
static void Main(string[] args)
{
MyStruct1 st1;
st1.i = 55;
Console.WriteLine(st1.ToString()); <----- this will call overridden ToString() in MyStruct1...

....
public struct MyStruct1
{
public int i;
public override string ToString()
{
return i.ToString(); <----- this, anyway, will not be boxed, right? (because compiler knows i is an int)

No comments: