基于可为空类型的对象仅在对象为非空时才装箱

Objects based on nullable types are only boxed if the object is non-null

我创建了一个简单的 C# 程序:

class Program
{
    static void Main(string[] args)
    {
        Int32? a = null;
        object x = a;
    }
}

根据 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.

我在 ILDASM 中尝试了我的可执行文件,发现 IL 代码调用了 box 方法。

.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

我的问题是:为什么叫它? 也许我做错了什么或误解了什么?

尝试对值进行装箱并不意味着它实际上是装箱值 - x 将是 null,而不是装箱 null。我认为 MSDN 试图解释:

Int32? a = null;
object x = a;
object y = a;
object.ReferenceEquals(x, y); // true

但是:

Int32? a = 3;
object x = a;
object y = a;
object.ReferenceEquals(x, y); // false

至于在发布模式下编译 - 它 可能 不会尝试装箱值,因为在编译时已知 anull -如果 a 是 public 方法的参数,它可能总是会尝试装箱该值(但永远不会真正装箱 null)。

您正在调试模式下编译代码。将其更改为发布,您将获得所需的行为。在这种情况下,它将一起省略赋值:

.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

如果您稍微更改代码并尝试接受 Int? 作为方法的参数,并在编译时显式传递 null,发布模式下的编译器将发出 box指令。

鉴于:

public void M(int? x) 
{
    object y = x;
    Console.WriteLine(y);
}
public void Main()
{
    M(null);
}

你会看到:

.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