C# 确保有效的枚举值 - 面向未来的方法

问题描述 投票:0回答:4

我正在使用基于枚举值的简单 switch 语句编写一些代码。 我想到,在将来的某个时候,开发人员可能会添加新值,因此我包含了一个默认方法来在运行时捕获该值并引发异常。 但是我意识到每次输入这样的逻辑时都应该这样做,并且我只会在运行时而不是编译时看到此类问题。
我想知道是否可以添加一些代码来让编译器告诉开发人员在更新枚举值的情况下他们需要更新某些方法 - 而不仅仅是向枚举本身添加注释?

例如(下面的示例纯粹是理论性的;我从开发生命周期中选择了状态,以确保它是大多数人所熟悉的)。

public enum DevelopmentStatusEnum
{
    Development
    //, QA //this may be added at some point in the future (or any other status could be)
    , SIT
    , UAT
    , Production
}

    public class Example
    {
        public void ExampleMethod(DevelopmentStatusEnum status)
        {
            switch (status)
            {
                case DevelopmentStatusEnum.Development: DoSomething(); break;
                case DevelopmentStatusEnum.SIT: DoSomething(); break;
                case DevelopmentStatusEnum.UAT: DoSomething(); break;
                case DevelopmentStatusEnum.Production: DoSomething(); break;
                default: throw new StupidProgrammerException(); //I'd like the compiler to ensure that this line never runs, even if a programmer edits the values available to the enum, alerting the program to add a new case statement for the new enum value
            }
        }
        public void DoSomething() { }
    }
    public class StupidProgrammerException: InvalidOperationException { }

这有点学术性,但我认为它对于使我的应用程序健壮很有用。 有没有人尝试过这个/对如何实现这个有任何好的想法?

.net c#-4.0 enums
4个回答
13
投票

在这种情况下,我会尝试不使用枚举,而是使用具有公共静态只读字段的类,这些字段是该类的实例。例如,了解 .Net 框架如何处理颜色。有一个 Color 类,您可以使用 Color.Black、Color.Blue 等对象。它们不是常量,但提供几乎所有相同的好处。另外,它们还有常量所没有的其他好处。请参阅 C# 第 3 版语言规范,其中也对此进行了一些讨论。

但想法是你没有案例陈述。您向每个“枚举”成员添加足够的其他属性,以便该方法(DoSomething 或其他)可以正确处理它。当另一个开发人员想要添加另一个成员对象时,他需要提供所需的属性。我的例子:我需要一个“枚举”来表示用户可以在系统中执行的不同操作。需要检查这些操作的权限、记录等。我还需要父操作和子操作(重命名某些内容是编辑它的“一部分”等)、用于将操作分组在一起以进行过滤目的的抽象操作,以及特殊操作“全部”和“无”(没有一个是未定义的)。每个人都需要一个 ID 和数据库中的文本。如果有人发明了一种新的动作,我希望它仍然有效。我所做的就是这样的(省略了很多代码只是为了给你一个想法):

  public class Action
  {
    protected Action(bool Abstract, Action Parent, int ID, string Name, bool Undefined)
    { /* snip */ }
    protected Action(bool Abstract, Action Parent, int ID, string Name)
      : this(Abstract, Parent, ID, Name, false)
    { }
    //----------------------------------------------------------------------------------------
    public static readonly Action All = new Action(true, null, 0, "All");
    public static readonly Action None = new Action(false, All, 6, "(Undefined)", true);
    public static readonly Action Modifying = new Action(true, All, 1, "Modifying");
    public static readonly Action Creating = new Action(false, Modifying, 2, "Creating");
    public static readonly Action Deleting = new Action(false, Modifying, 3, "Deleting");
    public static readonly Action Editing = new Action(false, Modifying, 4, "Editing");
    public static readonly Action Exporting = new Action(false, All, 5, "Exporting");
    public static readonly Action Renaming = new Action(false, Editing, 7, "Renaming");
    /* snip */
    //----------------------------------------------------------------------------------------
    /* template for new entries:
    public static readonly Action  = new Action(false, All, , "");
    */
  }

还有更多行动。其他类中有许多对操作进行操作的方法。只要每个操作提供了所需的信息,他们都会继续工作。添加操作的开发人员被迫提供信息。如果当前属性不足以执行某些未来的“特殊”操作,则稍后必须添加更多属性。请注意,构造函数是受保护的,因此只有类本身可以创建操作。我在主构造函数中省略了许多检查重复 ID 和名称以及许多其他内容的代码。您现在可以像这样使用它:

Log.LogAction(Action.Renaming);

LogAction 方法中没有 case 语句。它使用操作的属性。

您的枚举是多少?

问候


3
投票

我找到了一个使用简单枚举的“解决方案”。这是原始的,也许可以通过更好的设计来改进:

bool allCasesHandled;

switch (myEnumValue)
{
    case MyEnum.Value1:
        allCasesHandled = true;
        break;

    //default:
    //  allCasesHandled = true;
    //  break;
}
System.Diagnostics.Debug.WriteLine(allCasesHandled);

如果您尝试编译它,您将收到错误“使用未分配的变量”。

这维护起来有点麻烦,但对于简单的情况,它可能很有用,特别是在意味着失败的行上有注释。


2
投票

我可能是错的,但我不认为编译器会提供这样的警告。您可以通过一些单元测试来捕获此类问题,这些单元测试会调用类似于上面的方法以及所有可能的枚举值的方法(为此使用 Enum.GetValues() )。每当开发人员添加一个枚举成员并忘记修改所有 switch 语句时,至少有一个单元测试会因“StupidProgrammerException”而失败(顺便说一句:我会抛出 ArgumentOutOfRangeException)。


0
投票

我认为您可以为 StyleCop 编写规则并在 PostBuild 事件中运行它们,它们可以在 Build 窗口中输出警告。 我们不久前就开始尝试添加忽略方法返回值的警告,但从未抽出时间来完成它。 最后我研究了一下,您需要分析 IL,这并不总是有趣。 当然,我想这取决于你对乐趣的定义。

© www.soinside.com 2019 - 2024. All rights reserved.