CIL unbox_any指令 - 奇怪的行为

Dud*_*eti 5 .net clr il cil nullreferenceexception

.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
Run Code Online (Sandbox Code Playgroud)

相同的C#代码是:

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

我的问题是:

  1. 为什么unbox.any被称为?如果你这样做

     var a = father as child 
    
    Run Code Online (Sandbox Code Playgroud)

    isinst intruction将调用而不是unbox.any,如果我将删除泛型定义并且我将尝试将(isinst)对象强制转换为某个类,则不会调用unbox.any.

  2. 也许因为泛型定义而调用unbox.any,所以在这种情况下unbox.any需要抛出NullReferenceException,因为isinst指令的答案为此转换返回null.请参阅unbox_any.如果您尝试运行此代码,您将看到没有抛出任何异常.

更新

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

我的问题是,为什么不在unbox.any中抛出异常,如果我们尝试unbox到T的obj是null?

文档说:"如果obj是一个空引用,则抛出NullReferenceException."

Eri*_*ert 5

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

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

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