【问题标题】:Is there a way to get the C# compiler to emit an error if a switch(enum_val) is missing a case statement?如果 switch(enum_val) 缺少 case 语句,有没有办法让 C# 编译器发出错误?
【发布时间】:2011-05-09 04:30:04
【问题描述】:

我刚刚意识到我在枚举中的“必须处理”值列表中添加了一个值,但直到运行时我才发现它。我知道 C# 编译器在类型的反射和自省方面非常强大,所以我想知道是否有办法强制 switch/case 语句覆盖所有可能的 enum 值?

例子:

enum Colors
{
   Red,
   Blue,
   Green,
   Yellow
};

Colors c = ...;

switch (c)
{
   case Colors.Red:  // No error, Red is a Color
      break;
   case Colors.Blue:
   case Colors.Green:  // No error, Blue and Green handled as well
      break;
}  // whoops! "error: 'Colors.Yellow' unhandled"
   // or even, "error: no 'default' and 'Colors.Yellow' unhandled"

我想要一个编译时解决方案。

【问题讨论】:

  • 我能想到的一种方法是创建一个抽象类,该类具有特定于每个枚举值的行为实现。虽然这会增加类的数量,但它会确保不会错过枚举值的实现。如果您需要一些代码示例,请告诉我。

标签: c#


【解决方案1】:

您可以使用SwitchEnumAnalyzer 获取此类编译器时警告。我只是在我的项目中使用它,它工作得很好。与任何 Roslyn 分析器一样,您可以选择通知级别 - 如果它应该只是警告,还是适当的错误。

【讨论】:

【解决方案2】:

不,没有编译时方法可以做到这一点。然而,非常简单的答案是有一个default 处理程序,它只是抛出一个异常,“这个选项没有被处理,嘘”。

switch (c)
{
    case Colors.Red:  // no error, Red is a Color
        break;
    case Colors.Blue:
    case Colors.Green:  // no error, Blue and Green handled as well
        break;
    default:
        throw new Exception("Unhandled option: " + c.ToString());
}

【讨论】:

  • 但这会将静态验证变成运行时失败。
  • 问题是关于编译错误。不是运行时错误!!
  • @AliYousefi 感谢您及时的建设性反馈。
【解决方案3】:

这正是我们需要在所有解决方案中进行测试的最佳示例。您必须编写一个可以枚举您的 Enum 并调用包含 switch case 的方法的测试。使用此测试,每次编辑 Enum 但忘记更新 switch case 时,您都会得到一个失败的测试。

    [Test]
    public void CheckThatAllEnumCoveredInSwitchCase()
    {

        foreach (var singleEnum in Enum.GetValues(typeof(YOURENUM)))
        {
                myclass.methodofswitchcase((YOURENUM)singleEnum);
        }

    }

【讨论】:

  • 对于那些认为将每个值 singleEnum 装在引用类型中很丑陋的人,可以使用 foreach (var singleEnum in (YOURENUM[])(Enum.GetValues(typeof(YOURENUM)))) { ... } 避免这种情况。
【解决方案4】:

如果枚举大小,您可以尽早抛出异常,希望在对枚举进行更改时尽早提醒您:

enum MyEnum {A, B};

class TestEnum
{
    // Static constructor
    static TestEnum()
    {
        // Check if this code needs updating as the enum has changed
        if (Enum.GetNames(typeof(MyEnum)).Length != 2)
        {
            // If this fails update myFunction and the number above
            throw new Exception("Internal error - code inconsistency");
        }
    }

    // My function that switches on the enum
    string myFunction(MyEnum myEnum)
    {
        switch (myEnum)
        {
            case MyEnum.A: return "A";
            case MyEnum.B: return "B";
        }
        throw new Exception("Internal error - missing case");
    }
}

如果枚举中的项目数发生更改,这将从静态构造函数中引发异常。所以开发人员知道他需要更新代码。您甚至可以通过您的构建运行的 unit test 进行此检查。

【讨论】:

    【解决方案5】:

    C# 编译器没有内置此检查,但有一个适用于 .Net 的代码协定检查器:https://blogs.msdn.microsoft.com/francesco/2014/09/12/how-to-use-cccheck-to-prove-no-case-is-forgotten/

    技术是使用代码契约断言告诉检查器default 的情况永远不可到达:

    switch (c) {
      case Colors.Red: break;
      case Colors.Blue:
      case Colors.Green: break;
      default:
        Contract.Assert(false); // Tell static checker this shouldn't happen
    }
    

    然后,如果检查器发现它是可访问的(因为其中一个枚举值没有被处理),它会警告你:

    warning : CodeContracts: This assert, always leading to an error, may be reachable.
    Are you missing an enum case?
    

    【讨论】:

      【解决方案6】:

      您可以使用在运行时检查的元方法,但至少检查整个开关。

      https://github.com/faisalmansoor/MiscUtil/blob/master/EnumSwitch/EnumSwitch.cs

      【讨论】:

        【解决方案7】:

        对于 C++,您可以通过将编译器开关 /we4061 添加到项目设置中来让编译器发出警告。希望这可以帮助。 :)

        http://msdn.microsoft.com/en-us/library/thxezb7y.aspx

        http://msdn.microsoft.com/en-us/library/96f5t7fy(v=vs.80).aspx

        其实我错了;看来您可以让编译器抛出错误。只需使用 /Wall /we4061 编译。我自己没有尝试过,但是通过阅读上面的 MSDN 页面,它看起来应该可以工作。

        【讨论】:

        • 您提供的链接指的是 C/C++ 编译器
        • 这基本上就是我要找的... ...仅适用于 C#。 :)
        【解决方案8】:

        只需抛出(无意的双关语)这个,您就可以用字典替换 Switch-Case(以Func<int,int> 为例):

        Dictionary<Colors,Func<int,int>> d = new Dictionary<Colors, Func<int, int>>();
        d.Add(Colors.Red, (x) => x+1);
        d.Add(Colors.Blue, (x) => x+1);
        d.Add(Colors.Green, (x) => x+1);
        foreach (Colors color in Enum.GetValues(typeof(Colors)))
        {
            if (!d.ContainsKey(color))
            {
                throw new Exception("Poor color " + color + " ignored");
            }
        }
        

        【讨论】:

        • @maojlds,答案+1。 @mos,如果功能不像表示的那么简单,最好重构你的代码以使用OOP原则(继承&DPs)
        • @Splendor:如果有办法,我会这样做的。最后,我仍然需要处理捕获未实现的枚举值的运行时。
        • 我认为除了解决问题之外什么都做不了。它仍然会产生运行时错误而不是编译时错误,因此您没有回答问题。
        猜你喜欢
        • 1970-01-01
        • 2015-08-13
        • 1970-01-01
        • 1970-01-01
        • 2015-06-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-02
        相关资源
        最近更新 更多