为什么 C# 模式匹配对枚举并不详尽?

Joh*_*nyL 8 c# enums

说,我有以下枚举和代码测试枚举:

enum Flag
{
    On,
    Off
}

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF"
    };
Run Code Online (Sandbox Code Playgroud)

但是,我收到警告:

警告 CS8509 switch 表达式无法处理其输入类型的所有可能值(并非详尽无遗)。例如,不包括模式“(ConsoleApp.Flag)2”。

为什么当我列出所有枚举的值时它不是详尽无遗的?什么是(ConsoleApp.Flg)2枚举值?

war*_*ies 12

好消息!在 Roslyn 编译器的最新版本中,此警告(例如,(ConsoleApp.Flag)2未覆盖模式)已被赋予新代码 CS8524。

原始警告代码 CS8509 现在仅适用于缺少命名的枚举值。

因此,我们现在可以告诉编译器忽略 CS8524,我们认为为未命名枚举值编写一个包罗万象的处理程序是不必要的代码膨胀,但仍然希望捕获我们忘记处理命名值的情况(或者我们添加新的命名值到现有的枚举)。

另外,如果之前我们告诉编译器忽略 CS8509 以避免编写_ => throw ...处理程序,那么我们现在可能希望将其更改为忽略 CS8524,这样我们就可以在我们确实想要警告的情况下获得 CS8509 警告!

背景: Roslyn 的更改是在dotnet/roslyn#47066中进行的,我在阅读dotnet/csharplang#2671 的评论时发现了这一点(枚举上的 switch 表达式的可耗尽性应该不那么严格)


V0l*_*dek 11

反例:

string Foo()
{
    return GetMessage((Flag)42);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,C# 枚举不如 Haskell 或其他具有更好 FP 功能的语言中的代数数据类型(或变体类型,无论您喜欢如何称呼它们)健壮。它实际上只是围绕整数数值(int默认情况下)的一些元数据,因此类型系统中没有任何内容阻止您传递与有效枚举值不对应的值。编译器会告诉你,使用(Flag)2一个可能的值。要解决这个问题,添加一个标准的包罗万象的:

string GetMessage(Flag flag) =>
    flag switch
    {
        Flag.On  => "State is ON",
        Flag.Off => "State is OFF",
        _        => throw new ArgumentOutOfRangeException(nameof(flag)),
    };
Run Code Online (Sandbox Code Playgroud)

  • 如果重点是警告新的、未处理的值,那么这将如何运作?如果我们不添加默认臂,即使它不正确,我们也总是会看到警告。如果我们添加默认臂,即使我们需要它,我们也永远不会看到警告。所以这个设计根本没有帮助。 (9认同)
  • @aepot 是的,确实如此。并且不会使我的陈述变得不那么真实=) (3认同)