切换 var/null 奇怪的行为

switch with var/null strange behavior

给定以下代码:

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 的计算结果为 false。 VS Code 上的 IntelliSense 告诉我 o 也是 string。有什么想法吗?


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

我在这里汇总了多条 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 之间如此 非常 不同是很奇怪的,但这是编译器当前所做的。

在模式匹配 switch 语句中使用 case 作为显式类型是在询问所讨论的值是该特定类型还是派生类型。它完全等同于 is

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

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

虽然在模式匹配中 var 类型充当通配符,但会匹配包括 null 在内的任何值。

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

乍一看,编译没有任何警告似乎很奇怪(绝对让我失望)。但这与可追溯到 1.0 的 C# 行为相匹配。编译器允许 default 个案例,即使它可以简单地证明它永远不会被击中。考虑以下示例:

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

这里 default 永远不会被命中(即使 bool 的值不是 1 或 0)。然而 C# 从 1.0 开始就在没有警告的情况下允许这样做。模式匹配只是符合这里的这种行为。

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

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