删除unbox_any的抖动逻辑

本文关键字:抖动 any unbox 删除 | 更新日期: 2023-09-27 18:27:25

我正在研究这个C#代码的执行:

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

等效的IL代码为:

.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

基于这个答案(不必要的unbox_any),有人能向我解释Jitter在这里做的确切逻辑是什么吗;在这种特定情况下,Jitter是如何决定忽略"unbox_any"指令的(理论上,根据msdn,当第一条指令产生null时,应该抛出NullReferenceException,但这在实践中不会发生!)

更新

根据usr答案和Hans注释,如果obj是引用类型,则会调用castclass,因此不会调用NRE。

但是下面的情况呢?

static void Test<T>(object o) where T : new()
    {
        var nullable = o as int?;
        if (nullable != null)
            //do something
    }
Test<int?>(null);

以及等效的IL代码(部分):

IL_0001:  ldarg.0
IL_0002:  isinst     valuetype [mscorlib]System.Nullable`1<int32>
IL_0007:  unbox.any  valuetype [mscorlib]System.Nullable`1<int32>
IL_000c:  stloc.0
IL_000d:  ldloca.s   nullable
IL_000f:  call       instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
IL_0014:  stloc.1
IL_0015:  ldloc.1
IL_0016:  brfalse.s  IL_0024

在这种情况下,它的值类型为什么不抛出NRE?

删除unbox_any的抖动逻辑

当应用于引用类型时,unbox.any指令与castclass typeTok具有相同的效果。

CCD_ 2被约束为引用类型。在这种情况下,此指令不会抛出NRE。JIT不会"忽略"它,而是按照指定执行它。JIT不允许忽略指令。

文件中有声明

如果obj是null引用,则引发NullReferenceException。

这是误导性的,因为它只适用于值类型。我引用的第一句话毫不含糊。

要回答可为null值类型的第二种情况(问题的更新部分),我们需要仔细查看ECMA CLI规范(III.4.33 unbox.any–将装箱类型转换为值):

如果obj为null且typeTok为不可为null的值类型,则抛出System.NullReferenceException

MSDN文档中缺少粗体部分。

因此,总结unbox_any的行为:

  1. 如果typeTok是ref类型,则行为与castclass相同
  2. 如果typeTok是值类型:

    2.1.如果obj为null,typeTok为可为null的值类型,则结果为null

    2.2.如果obj为null并且typeTok不是可为null的值类型,则NullReferenceException将抛出

如果我理解正确,第2.2段的行为与常规开箱操作相同,请参阅抖动源代码