切换与var / null奇怪的行为

问题描述 投票:89回答:3

给出以下代码:

string someString = null;
switch (someString)
{
    case string s:
        Console.WriteLine("string s");
        break;
    case var o:
        Console.WriteLine("var o");
        break;
    default:
        Console.WriteLine("default");
        break;
}

为什么switch语句匹配case var o

据我所知,case string ss == null不匹配,因为(有效)(null as string) != null评估为假。 VS Code上的智能感知告诉我o也是string。有什么想法吗?


类似于:C# 7 switch case with null checks

c# null switch-statement var c#-7.0
3个回答
68
投票

在使用switch作为显式类型匹配case语句的模式内部,询问所讨论的值是否属于该特定类型或派生类型。它与is完全相同

switch (someString) {
  case string s:
}
if (someString is string) 

null没有类型,因此不满足上述任一条件。在任何一个例子中,someString的静态类型都没有发挥作用。

var类型虽然在模式匹配中充当外卡并且将匹配任何值,包括null

这里的default案例是死代码。 case var o将匹配任何值,null或非null。非默认情况总是胜过默认情况,因此default永远不会被击中。如果你看一下IL,你会发现它甚至都没有发出。

一目了然,这可能看起来很奇怪,这个编译没有任何警告(肯定会让我离开)。但这与C#行为相匹配,可以追溯到1.0。编译器允许default案例,即使它可以简单地证明它永远不会被击中。以下面的例子为例:

bool b = ...;
switch (b) {
  case true: ...
  case false: ...
  default: ...
}

在这里default永远不会被击中(即使对于值为1或0的bool)。然而,C#在没有任何警告的情况下允许1.0。模式匹配正好符合这种行为。


22
投票

我在这里汇集了多个Twitter评论 - 这对我来说实际上是新的,我希望jaredpar能够提供更全面的答案,但是;我理解的简短版本:

case string s:

被解释为if(someString is string) { s = (string)someString; ...if((s = (someString as string)) != null) { ... } - 其中任何一个涉及null测试 - 在你的情况下失败;反过来:

case var o:

编译器解析o作为string只是o = (string)someString; ... - 没有null测试,尽管表面看起来很相似,只是提供类型的编译器。

最后:

default:

这里无法到达,因为上面的案例抓住了一切。这可能是一个编译器错误,因为它没有发出无法访问的代码警告。

我同意这是非常微妙和细微的,令人困惑的。但显然case var o场景具有空传播(o?.Length ?? 0等)。我同意奇怪的是,这在var ostring s之间的工作方式非常不同,但它正是编译器目前所做的。


14
投票

这是因为case <Type>匹配动态(运行时)类型,而不是静态(编译时)类型。 null没有动态类型,所以它无法与string匹配。 var只是后备。

(发帖因为我喜欢简短的答案。)

© www.soinside.com 2019 - 2024. All rights reserved.