繁体   English   中英

为什么 C# 模式匹配对于枚举不是详尽无遗的?

[英]Why C# pattern matching is not exhaustive for enums?

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

enum Flag
{
    On,
    Off
}

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

但是,我收到警告:

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

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

反例:

string Foo()
{
    return GetMessage((Flag)42);
}

不幸的是,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)),
    };

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

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

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

此外,如果之前我们告诉编译器忽略 CS8509 以避免编写_ => throw...处理程序,我们现在可能希望将其更改为忽略 CS8524,以便我们为我们确实需要警告的情况返回 CS8509 警告!

背景: Roslyn 更改是在do.net/roslyn#47066中进行的,这是我在阅读do.net/csharplang#2671的评论时发现的(枚举上开关表达式的穷尽性应该不那么严格)

这可以通过#pragma指令来做到详尽无遗。 例子:

public enum SoundEffect
{ 
    NOTE_0,
    NOTE_1,
    NOTE_2,
    NOTE_3,
    NOTE_4,
    CHIME,
}

AudioClip GetAudioClip(SoundEffect soundEffect)
{
    #pragma warning disable CS8524
    return soundEffect switch
    {
        SoundEffect.NOTE_0 => Notes[0],
        SoundEffect.NOTE_1 => Notes[1],
        SoundEffect.NOTE_2 => Notes[2],
        SoundEffect.NOTE_3 => Notes[3],
        SoundEffect.NOTE_4 => Notes[4],
        SoundEffect.CHIME => Chime,
    };
    #pragma warning restore CS8524
}

然后,您添加到SoundEffect (在上面的示例中)的任何新值都将触发编译器警告。

您可以通过 C# 编译器选项将该编译器警告转换为错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM