切换与var/null奇怪的行为

bud*_*udi 90 c# null var switch-statement c#-7.0

给出以下代码:

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

为什么switch语句匹配case var o?

因为(有效地)评估为false case string s,s == null所以我的理解不匹配(null as string) != null.VS Code上的IntelliSense告诉我这o也是一个string.有什么想法吗?


类似于:C#7切换案例,带有空检查

Jar*_*Par 69

在switch使用casefor显式类型的模式匹配语句中,询问所讨论的值是否属于该特定类型或派生类型.它完全等同于is

switch (someString) {
  case string s:
}
if (someString is string) 
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

bool b = ...;
switch (b) {
  case true: ...
  case false: ...
  default: ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • @JaredPar感谢这里的见解; 我个人会支持更多警告,即使之前没有这样做,但我理解语言团队的限制.你有没有考虑过"关于一切模式的抱怨"(默认情况下可能是开启),与"传统的斯多葛模式"(选修)?也许是`csc/stiffUpperLip` (5认同)
  • 真正的问题是编译器"显示"`var`是`string`的类型,当它真的不是时(老实说不确定应该是什么类型) (4认同)
  • @JonathanDickinson我不认为这表明你的想法.这只是显示一个`null`是一个有效的`string`引用,并且任何`string`引用(包括`null`)都可以隐式地转换(引用保留)到`object`引用,以及任何`object`引用是'null`可以成功地向任何其他类型进行upcast(显式),仍然是`null`.在编译器类型系统方面并不是一回事. (4认同)
  • @MarcGravell我们有一个名为警告波的功能,旨在使其更容易,更简洁,提供新的警告.基本上每个编译器都是新浪潮,您可以通过/ wave:1,/ wave:2,/ wave:all选择警告. (3认同)

Mar*_*ell 22

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

case string s:
Run Code Online (Sandbox Code Playgroud)

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

case var o:
Run Code Online (Sandbox Code Playgroud)

编译器在哪里解决o的string仅仅是o = (string)someString; ...-没有null测试,尽管事实上,它表面上看起来相似,只是编译器提供的类型.

最后:

default:
Run Code Online (Sandbox Code Playgroud)

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

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


Meh*_*dad 14

这是因为动态(运行时)类型case <Type>匹配,而不是静态(编译时)类型.没有动态类型,因此无法匹配.只是后备.nullstringvar

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