I have created a simple C#
program:
class Program
{
static void Main(string[] args)
{
Int32? a = null;
object x = a;
}
}
According to MSDN:
Objects based on nullable types are only boxed if the object is non-null. If HasValue is false, the object reference is assigned to null instead of boxing.
I have tried my executable in ILDASM
and spotted that IL
code has box method called.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 17 (0x11)
.maxstack 1
.locals init ([0] valuetype [mscorlib]System.Nullable`1<int32> a,
[1] object x)
IL_0000: nop
IL_0001: ldloca.s a
IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0009: ldloc.0
IL_000a: box valuetype [mscorlib]System.Nullable`1<int32>
IL_000f: stloc.1
IL_0010: ret
} // end of method Program::Main
My question is: Why it was called? Maybe I am doing something wrong or misunderstand something?
The attempt to box a value does not mean it actually is a boxed value - x
will be null
, not a boxed null
. I think the MSDN is trying to explain that:
Int32? a = null;
object x = a;
object y = a;
object.ReferenceEquals(x, y); // true
But:
Int32? a = 3;
object x = a;
object y = a;
object.ReferenceEquals(x, y); // false
As for compiling in release mode - it might not attempt to box the value because at compile time it is known that a
is null
- if a
was a parameter of a public method, it will probably always attempt to box the value (but never actually box null
).
You're compiling your code in Debug mode. Change it to Release and you'll get the desired behavior. In this case, it'll omit the assigment all together:
.method private hidebysig static
void Main (string[] args
) cil managed
{
// Method begins at RVA 0x2054
// Code size 11 (0xb)
.maxstack 1
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<int32>
)
IL_0000: ldloca.s 0
IL_0002: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0008: ldloc.0
IL_0009: pop
IL_000a: ret
} // end of method C::Main
If you change your code a bit and attempt to accept a Int?
as a parameter to a method, and explicitly pass null
at compile time, the compiler in Release mode will emit a box
instruction.
Given:
public void M(int? x)
{
object y = x;
Console.WriteLine(y);
}
public void Main()
{
M(null);
}
You'll see:
.method public hidebysig
instance void M (
valuetype [mscorlib]System.Nullable`1<int32> x
) cil managed
{
// Method begins at RVA 0x2050
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.1
IL_0001: box valuetype [mscorlib]System.Nullable`1<int32>
IL_0006: call void [mscorlib]System.Console::WriteLine(object)
IL_000b: ret
} // end of method C::M
.method public hidebysig
instance void Main () cil managed
{
// Method begins at RVA 0x2060
// Code size 16 (0x10)
.maxstack 2
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<int32>
)
IL_0000: ldarg.0
IL_0001: ldloca.s 0
IL_0003: initobj valuetype [mscorlib]System.Nullable`1<int32>
IL_0009: ldloc.0
IL_000a: call instance void C::M(valuetype [mscorlib]System.Nullable`1<int32>)
IL_000f: ret
} // end of method C::Main
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With