8

列挙値に基づく単純な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 { }

これは少しアカデミックですが、アプリを堅牢にするのに役立つと思います。誰かがこれを以前に試したことがありますか/これがどのように達成されるかについて何か良いアイデアがありますか?

前もって感謝します、

JB

4

4 に答える 4

13

このような場合、列挙型ではなく、クラスのインスタンスであるpublicstaticreadonlyフィールドを持つクラスを使用しようとします。たとえば、.NetFrameworkが色で何をするかをご覧ください。Colorクラスがあり、Color.Black、Color.Blueなどのオブジェクトを使用できます。これらは定数ではありませんが、ほぼすべて同じ利点を提供します。さらに、定数にはない他の利点もあります。これについても少し説明しているC#バージョン3の言語仕様を参照してください。

しかし、アイデアはあなたがcaseステートメントを持っていないということです。メソッド(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ステートメントが含まれていません。アクションの属性を使用します。

あなたの列挙は何ですか?

よろしく

于 2012-10-01T15:04:29.573 に答える
3

プレーンな列挙型を使用した「ソリューション」を見つけました。これは、生の、より良い設計で改善できるかもしれません:

bool allCasesHandled;

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

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

これをコンパイルしようとすると、「割り当てられていない変数の使用」というエラーが発生します。

これを維持するのは少し面倒ですが、単純な場合には、特に失敗することを意図した行にコメントを付けると便利な場合があります。

于 2012-10-03T10:08:21.380 に答える
2

私は間違っているかもしれませんが、コンパイラがそのような警告を出すとは思いません。このような問題は、考えられるすべての列挙値を使用して上記のようなメソッドを呼び出す単体テストで見つけることができます(これにはEnum.GetValues()を使用します)。開発者が列挙型メンバーを追加し、すべてのswitchステートメントを変更するのを忘れるたびに、少なくとも1つの単体テストが「StupidProgrammerException」で失敗します(ところで、ArgumentOutOfRangeExceptionをスローします)。

于 2012-05-22T19:39:30.240 に答える
0

StyleCopのルールを記述して、PostBuildイベントで実行でき、ビルドウィンドウに警告を出力できると思います。メソッドの戻り値を無視するための警告を追加するために、しばらく前にこれを試み始めましたが、それを終了することはできませんでした。最後に調べたところ、ILを分析する必要がありましたが、これは必ずしも楽しいとは限りません。もちろん、それはあなたの楽しみの定義に依存すると思います。

于 2012-10-03T15:59:33.260 に答える