给出以下代码:
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 s
在s == null
时不匹配,因为(有效)(null as string) != null
评估为false。 VS Code上的IntelliSense告诉我o
也是string
。有什么想法吗?
答案 0 :(得分: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
永远不会被点击(即使bool
的值不是“1”或“0”。然而,C#在没有任何警告的情况下允许1.0。模式匹配正好符合这种行为。
答案 1 :(得分: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 o
和string s
之间的非常不同,但它正是编译器目前所做的。
答案 2 :(得分:14)
这是因为case <Type>
匹配动态(运行时)类型,而不是静态(编译时)类型。 null
没有动态类型,因此无法与string
匹配。 var
只是后备。
(发帖因为我喜欢简短的答案。)