在C#中,`x is int?`和`x is int`之间有区别吗?

con*_*low 11 c# boxing types cil nullable

class C<T> where T : struct {
    bool M1(object o) => o is T;
    bool M2(object o) => o is T?;
}
Run Code Online (Sandbox Code Playgroud)

传递null引用或盒装T值时,上述两种方法似乎表现相同.但是,生成的MSIL代码有点不同:

.method private hidebysig instance bool M1(object o) cil managed {
    .maxstack 8
    IL_0000: ldarg.1
    IL_0001: isinst !T
    IL_0006: ldnull
    IL_0007: cgt.un
    IL_0009: ret
}
Run Code Online (Sandbox Code Playgroud)

VS

.method private hidebysig instance bool M2(object o) cil managed {
    .maxstack 8
    IL_0000: ldarg.1
    IL_0001: isinst valuetype [mscorlib]System.Nullable`1<!T>
    IL_0006: ldnull
    IL_0007: cgt.un
    IL_0009: ret
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,o is T?表达式实际上对Nullable<T>类型执行类型检查,尽管CLR专门处理可空类型,因此C#表示盒装T?值作为null引用(如果T?没有值)或盒装T值.似乎不可能Nullable<T>在纯C#或甚至在C++/CLI 中获得类型的盒子(因为运行时处理box操作码以支持这种" T?=> T盒子/ null"拳击).

我在C#中遗漏了什么或o is T?几乎等同o is T于?

Ale*_*exD 7

根据规范(强调我的),in E is T,非可空值类型T和相应的可空类型以相同的方式处理:

7.10.10 is操作员

is操作者用于动态检查是否一个对象的运行时类型是与给定类型兼容.操作的结果E is T,其中E是表达式并且T是类型,是一个布尔值,指示是否E可以T通过引用转换,装箱转换或取消装箱转换成功转换为类型.在使用类型参数替换所有类型参数之后,将按如下方式评估操作:

  • 如果E是匿名函数,则发生编译时错误

  • 如果E是方法组或空文字,如果类型E是引用类型或可空类型且E的值为null,则结果为false.

  • 否则,让我们D表示动态类型E如下:

    • 如果类型E是引用类型,D则是实例引用的运行时类型E.
    • 如果类型E是可空类型,D则是该可空类型的基础类型.

    • 如果类型E是非可空值类型,D则是类型E.

  • 操作的结果取决于DT如下:

    • 如果T是引用类型,其结果是,如果真DT是相同的类型,如果D是引用类型和从隐式引用转换DT存在,或者如果D是一个值类型和从一个装箱转换DT存在.
    • 如果T是可空类型,则结果为true,如果D是基础类型T.
    • 如果T是非可空值类型,则结果为true,如果DT是相同类型.
    • 否则,结果为false.