CIL unbox_any 指令 - 奇怪的行为

CIL unbox_any instruction - strange behavior

.method public static void  Test<class T>(object A_0) cil managed
{
  // Code size       13 (0xd)
  .maxstack  1
  .locals init (!!T V_0)
  IL_0000:  ldarg.0
  IL_0001:  isinst     !!T
  IL_0006:  unbox.any  !!T
  IL_000b:  stloc.0
  IL_000c:  ret
} // end of method DemoType::Test

相等的C#代码是:

public static void Test<T>(object o) where T : class
{
    T t = o as T;
}

我的问题是:

  1. 为什么叫unbox.any?如果你只是做

     var a = father as child 
    

    isinst 指令将调用并且没有 unbox.any,并且如果我将删除通用定义并且我将尝试将对象转换 (isinst) 到某些 class,没有 unbox.any 将被调用。

  2. 可能 unbox.any 被调用是因为泛型定义,所以在这种情况下 unbox.any 需要抛出 NullReferenceException 因为 isinst 指令的答案 return null对于这个铸造。参见 unbox_any。如果您尝试 运行 这段代码,您将看到没有抛出任何异常。

更新

我可以理解 unbox_any 因为对象类型参数,它会在 isinst 检查后尝试将其转换为具体类型。也许仿制药也有影响。

我的问题是,如果我们尝试拆箱到 T 的对象为空,为什么不在 unbox.any 中抛出异常?

文档说:"NullReferenceException is thrown if obj is a null reference."

开箱是为了让验证者开心。验证器在知道类型参数 T 始终是引用类型方面并不是特别聪明,因此 C# 编译器会发出这些不必要的拆箱。

如果您在 Roslyn 源代码中搜索 Unbox_any 和 IsVerifierReference,您会发现这种情况发生在代码生成器的很多地方。

抖动会在生成代码时知道类型参数是否是引用,并且应该生成像样的代码而不考虑看似不必要的指令。