为什么在 Visual Studio 2019(16.0.3 之前版本)中,针对字符串常量的 switch-case 语句需要一个默认值,而在 Visual Studio 2017 中则不需要?

11

我正在尝试在使用Visual Studio 2017编写的代码库中使用Visual Studio 2019,并且立即发现了构建问题。我有一个switch case语句,其中情况是根据常量字符串选择的。这在Visual Studio 2017中没有默认情况下是可以的,但在Visual Studio 2019中会引发构建错误。

我可以通过添加默认情况来解决此问题,但如果可能的话,我想避免更改代码并只更改编译器设置,以避免需要拉取请求。无论如何,理解问题的原因都很重要。

public class Program
{
    public const string Database = "MongoDB";

    public static string GetDb()
    {
        switch (Database)
        {
            case "MongoDB":
                return Database;
        }
    }
}

可以在Github存储库中找到包含示例解决方案的代码,网址为:https://github.com/martineyles/NoDefaultCase。其中包括示例解决方案在添加到Github之前的存档。

在Visual Studio 2017中,构建的输出为:

1>------ Rebuild All started: Project: NoDefaultCase, Configuration: Debug Any CPU ------
1>  NoDefaultCase -> C:\Users\MartinEyles\source\repos\NoDefaultCase\NoDefaultCase\bin\Debug\NoDefaultCase.exe
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
在 Visual Studio 2019 中,构建的输出为:
1>------ Rebuild All started: Project: NoDefaultCase, Configuration: Debug Any CPU ------
1>C:\Users\MartinEyles\source\repos\NoDefaultCase\NoDefaultCase\Program.cs(9,30,9,35): error CS0161: 'Program.GetDb()': not all code paths return a value
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========

我正在针对 .net framework 4.7.2 和默认语言版本进行开发。我还尝试将语言版本降低到 C# 6.0,并手动将语言版本设置为 C# 7.3,但结果相同。

我使用的 Visual Studio 版本是:

Microsoft Visual Studio Enterprise 2017 
Version 15.9.11
VisualStudio.15.Release/15.9.11+28307.586
Microsoft .NET Framework Version 4.7.03056

Microsoft Visual Studio Enterprise 2019 
Version 16.0.0
VisualStudio.16.Release/16.0.0+28729.10
Microsoft .NET Framework Version 4.7.03056

问题已在以下版本中解决:

Microsoft Visual Studio Enterprise 2019
Version 16.0.3
VisualStudio.16.Release/16.0.3+28803.352
Microsoft .NET Framework Version 4.7.03056

1
它是给你一个构建错误还是构建警告?默认情况下不应该需要一个。 - user47589
如果我将MongoDatabase替换为其他内容,它可以在2019年使用C#语言版本7.3编译。 - user47589
1
好的,你说得对。这个问题确实出现在VS2019的非预览版本中。你应该向微软提出此问题。同时,考虑在VS 2019中使用C# 8.0 beta编译器-它不会出现这个问题。 - mjwills
1
这个问题也已经被记录在以下网址:https://developercommunity.visualstudio.com/content/problem/520522/switch-case-without-default-no-longer-compiles-in.html#但我不确定这是一个错误还是需要更改的设置,所以我认为它目前仍然具有相关性。 - Martin Eyles
1
似乎在16.0.3中已经修复。请参考https://learn.microsoft.com/en-us/visualstudio/releases/2019/release-notes#16.0.3 - Viktor Mellgren
显示剩余7条评论
2个回答

8
看起来规范将会更新关于可达性的新规则,或者这是Roslyn中的一个错误,可能由于引入了switch表达式的更改。
对编译器而言,重要的问题是方法的结尾是否可达——当且仅当switch语句的结尾可达时才是可达的。
ECMA C# 5标准13.8.3节描述了switch语句结尾的可达性:
“如果以下至少一项为真,则switch语句的结束点是可达的:1. switch语句包含可达的break语句,该语句退出switch语句;2. switch语句是可达的,switch表达式是非常量值,并且没有默认标签;3. switch语句是可达的,switch表达式是不匹配任何case标签的常量值,并且没有默认标签。”
在您的示例中似乎都不是这种情况。
  • 没有break语句
  • switch表达式是一个常量值
  • 常量值与case标签匹配

因此,根据C# 5规则,这个switch语句的结束点是不可到达的,应该可以编译通过。GitHub上的草案规范有相同的文本,所以看起来还没有改变...


1
它在C# 8(beta)中编译的事实表明这是一个错误。我想我们很快就会找出答案! - mjwills
2
这已经被Roslyn团队确认为一个bug。该问题在他们的GitHub页面上有所提及:https://github.com/dotnet/roslyn/issues/35011 - Martin Eyles
1
Visual Studio 16.0.3 发行说明 表明该问题已在此版本中得到解决,我可以确认示例代码在 16.0.3 中构建成功。 - Martin Eyles

0
在C# 6中,匹配表达式必须是返回以下类型之一的表达式: 字符。 字符串。 布尔值。 整数值,例如int或long。 枚举值。 从C# 7.0开始,匹配表达式可以是任何非空表达式。
文档说明了,在C# 7.0中,匹配表达式可以是任何非空表达式。在C# 7中,字符串成为可空类型,因此您必须添加默认情况(用于空情况)。

代码库使用了C# 7的特性,但在Visual Studio 2017中仍然可以编译通过。 - Martin Eyles
使用Visual Studio 2019的新功能: 一个switch表达式必须始终返回一个值。但是,即使这不是真的(即情况没有涵盖所有可能的值),代码仍将编译。 编译器只会对上述代码发出警告。如果在运行时测试的值未与任何情况匹配,则会引发InvalidOperationException异常。 https://www.dotnetcurry.com/csharp/1489/csharp-8-visual-studio-2019 - Alaaeddine HFIDHI
我收到了一个构建错误,而不是警告。 CS0161 C#“”: 并非所有代码路径都返回值。 - Martin Eyles
我尝试在新项目中提取相关代码,并在项目高级构建属性中设置C#语言版本为6.0,但在Visual Studio 2019中仍无法编译,出现相同的构建错误,并且在Visual Studio 2017中继续成功构建。 - Martin Eyles
1
一个switch表达式必须始终返回一个值。这段代码不包含一个switch表达式。 - mjwills
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接