我应该如何解释 C# 中的“空检查模式”

Nig*_*gel 14 c#

我有一些代码可以有效地做到这一点:

    private void DoStuff(int? a)
    {
        int c = 0;
        if (a is int b)
        {
            c = b;
        }
    }
Run Code Online (Sandbox Code Playgroud)

a is int b给了我一个警告:

使用非空模式而不是对任何非空值成功进行类型检查

使用 Resharper 的建议“使用空检查模式”会自动更正此代码,如下所示,从而导致警告消息消失:

   private void DoStuff(int? a)
    {
        int c = 0;
        if (a is { } b)
        {
            c = b;
        }
    }
Run Code Online (Sandbox Code Playgroud)

这很好,但现在我不明白我正在编写的代码。我该如何if(a is {} b)用英语来解释呢?

它是说“如果a不是 null 设置b为非a空值”吗?

或者是{}“底层类型a”(即int)的简写?

我可以在大括号内放什么东西吗?或者大括号本身有自己的含义吗?

任何可以帮助我理解这段代码真正含义的内容将不胜感激。谢谢。

Zze*_*Zze 13

这是一个用于将表达式与模式进行匹配a is { } 的示例...参考is

is 运算符检查表达式的结果是否与给定类型兼容。有关类型测试 is 运算符的信息,请参阅类型测试和强制转换运算符一文的 is 运算符部分。

从 C# 7.0 开始,您还可以使用 is 运算符将表达式与模式进行匹配,如以下示例所示:

static bool IsFirstFridayOfOctober(DateTime date) =>
    date is { Month: 10, Day: <=7, DayOfWeek: DayOfWeek.Friday }
Run Code Online (Sandbox Code Playgroud)

a is { } b是使用声明模式声明新的局部变量(作用域为if块)的示例。参考

您可以使用声明和类型模式来检查表达式的运行时类型是否与给定类型兼容。使用声明模式,您还可以声明一个新的局部变量。当声明模式与表达式匹配时,将为该变量分配转换后的表达式结果


我强烈推荐Sharplab.io来研究 C# 编译期间发生的优化。在您提供的示例中:

private void DoStuff(int? a)
{
    int c = 0;
    if (a is { } b)
    {
        c = b;
    }
}
Run Code Online (Sandbox Code Playgroud)

等于:

private void DoStuff(Nullable<int> a)
{
    int num = 0;
    int valueOrDefault = default(int);
    int num2;
    if (a.HasValue)
    {
        valueOrDefault = a.GetValueOrDefault();
        num2 = 1;
    }
    else
    {
        num2 = 0;
    }
    if (num2 != 0)
    {
        num = valueOrDefault;
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上a is { } b就是说;确实a匹配任何模式(即不为空)如果是,则将其分配给局部变量b


我个人认为,无论你编写什么代码,都应该尽可能让每个人都可读。如果您对所编写的代码感到困惑,那么就不要编写它,而编写一些接替您的人会理解的内容。


Sta*_*kop 7

TL;DR resharper 不正确,官方语言参考表明if (a is int b)。反正if (a is int b)if (a is { } b)编译成一模一样的CIL。


Resharper 的建议似乎是不正确的,甚至可空值类型的官方语言参考(链接)字面上显示了一个示例if (a is int valueOfA)

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
// Output:
// a is 42
Run Code Online (Sandbox Code Playgroud)

is甚至( link ) 运算符的官方语言参考也显示:

int i = 34;
object iBoxed = i;
int? jNullable = 42;
if (iBoxed is int a && jNullable is int b)
{
    Console.WriteLine(a + b);  // output 76
}
Run Code Online (Sandbox Code Playgroud)

另外,如果您检查CIL,它会在 .NET 7 中编译成完全相同的东西。在这里亲自看看。

if (a is { } b)

        IL_0000: nop
        IL_0001: ldc.i4.s 0
        IL_0003: stloc.0
        IL_0004: ldarga.s a
        IL_0006: call instance bool valuetype [System.Runtime]System.Nullable`1<int32>::get_HasValue()
        IL_000b: brfalse.s IL_0018

        IL_000d: ldarga.s a
        IL_000f: call instance !0 valuetype [System.Runtime]System.Nullable`1<int32>::GetValueOrDefault()
        IL_0014: stloc.1
        IL_0015: ldc.i4.1
        IL_0016: br.s IL_0019
Run Code Online (Sandbox Code Playgroud)

对比if (a is int b)

        IL_0000: nop
        IL_0001: ldc.i4.s 0
        IL_0003: stloc.0
        IL_0004: ldarga.s a
        IL_0006: call instance bool valuetype [System.Runtime]System.Nullable`1<int32>::get_HasValue()
        IL_000b: brfalse.s IL_0018

        IL_000d: ldarga.s a
        IL_000f: call instance !0 valuetype [System.Runtime]System.Nullable`1<int32>::GetValueOrDefault()
        IL_0014: stloc.1
        IL_0015: ldc.i4.1
        IL_0016: br.s IL_0019
Run Code Online (Sandbox Code Playgroud)