2009-03-06 4 views
16

列挙型の値を受け入れるメソッドがあるとします。このメソッドは、値が有効であることを確認した後、可能な値を超えてswitchとなります。だから質問は、予期しない値を扱う好ましい方法は何かの後に値の範囲が検証されていますか?例えば予期しない列挙型の値を処理するための好ましい方法は何ですか?

enum Mood { Happy, Sad } 

public void PrintMood(Mood mood) 
{ 
    if (!Enum.IsDefined(typeof(Mood), mood)) 
    { 
     throw new ArgumentOutOfRangeException("mood"); 
    } 

    switch (mood) 
    { 
     case Happy: Console.WriteLine("I am happy"); break; 
     case Sad: Console.WriteLine("I am sad"); break; 
     default: // what should we do here? 
    } 

defaultケースを処理する好ましい方法は何ですか?

  • 私が好む私は
+0

...それはすべての方法ダウンナチョスだ - あなたは常にそこに一緒にすべての小さなナチョ・オブジェクトを保持しているいくつかの大きな厄介な「チーズ核」ですが、あなたはそれを見つけることができないという感覚を得ます自分の意見であなたの意見が異なる場合、またはこのケースをサポートする別のケースがある場合は、あなたの知識を共有してください。質問の目的は、この状況に直面している他の人(そして私)を助けるために、トピックに関するアイデアを集めることです。 –

+0

なぜこれがJavaとマークされましたか? – OscarRyz

+0

@Oscar:言語に依存しない質問IMHOでなければなりません。人々はここで両方の言語の答えを見つけることができます。 –

答えて

10

と考えていないいくつかの他の方法

  • throw new NotImplementedException()(またはその他の例外)// can never happen
  • Debug.Fail()(またはDebug.Assert(false))のようなコメントを残しますthrow new NotImplementedException("Unhandled Mood: " + mood)。重要なのは、列挙が将来変更される可能性があり、この方法はそれに応じて更新されない可能性があることです。例外を投げるのが最も安全な方法です。

    メソッドがライブラリの一部であり、新しい値がデバッグモードでテストされない可能性があるので、Debug.Fail()メソッドが嫌いです。そのライブラリを使用している他のアプリケーションは、そのような場合には奇妙なランタイム動作に直面する可能性がありますが、例外をスローするとエラーがすぐに判明します。

    注:にはNotImplementedExceptionが存在します。

  • +0

    これは元の投稿に編集されているはずです。 –

    +1

    @ SnOrfus:なぜですか?それは完全に有効な答えです。 –

    +0

    私の答えで言ったように、それは有効かもしれませんが、それは間違ったOOの間違っている... –

    1

    デフォルトでは、渡された列挙型の値を呼び出すトレースを持つことができます。例外をスローするのは問題ありませんが、大規模なアプリケーションでは、コードが列挙型の他の値を気にしない場所がいくつかあります。
    したがって、コードが列挙型の可能な値をすべて処理する予定の場合は、後で戻って例外を削除する必要があります。 Javaでは

    7

    、標準的な方法は、2つの理由のために、AssertionErrorをスローすることです:

    1. これはassertsが無効になっている場合でも、エラーがスローされることを保証します。
    2. 他の列挙型の値がないと主張しているので、AssertionErrorは、NotImplementedException(Javaにはありません)よりも前提条件を文書化しています。私のコードベースではほとんどすべてのswitchステートメントの
    +0

    しかし、AssertionErrorはjava.lang.Exceptionではなくjava.lang.Errorを拡張します。これは、コード内の他のレイヤーが例外をキャッチしてそれから回復する機会を持たないことを意味します。 (技術的には可能ですが、実際にはほとんどの人がjava.lang.Errorを捕まえてはいけません)。 –

    +0

    すでに有効であるかどうかを確認しても、AssertionErrorは決して起こらないはずです(つまり、あなたは主張している)。もしそれが起これば、物事は間違っているので、それを捕まえることは無意味だと思う。しかし、時にはそれを捕まえることが有用かもしれないことがわかります。 –

    +0

    この質問の議論も参照してください:http://stackoverflow.com/questions/398953/java-what-is-the-preferred-throwable-to-use-in-a-private-utility-class-construct –

    2

    、私はこの方法は、エラーが検出された時点で、列挙型の値を詳述例外がスローされます、次のデフォルトの場合

    switch(value) { 
    ... 
    default: 
        Contract.InvalidEnumValue(value); 
    } 
    

    を持っています。

    public static void InvalidEnumValue<T>(T value) where T: struct 
    { 
        ThrowIfFalse(typeof(T).IsEnum, "Expected an enum type"); 
        Violation("Invalid Enum value of Type {0} : {1}", new object[] { typeof(T).Name, value }); 
    } 
    
    +0

    しかし、メソッドがそれを反映するように更新されていなくても、値は有効です。私は "無効な値"メッセージはここで少し誤解を招くと思います。 –

    +0

    @Hosamですが、そのコードがその価値を期待して書かれていなかったので、私はその名前を選んだのです。 「予期しない値」も同様に機能しますが、それはプログラマが意図した値ではないことをキャプチャすることを意味します。 – JaredPar

    +0

    @JaredPar:十分に公正です。私は、未処理のケース(IMHO)であることを明確にする「未処理の値」のラインに沿ってもっと考えていました。 –

    0

    は、意見や好み、それを呼び出しますが、列挙型の背後にある考え方は、それが可能な値の完全なリストを表していることです。コードで "予期しない値"が渡されている場合、列挙型(または列挙型の目的)は最新ではありません。私の個人的な好みは、すべての列挙型がUndefinedのデフォルト割り当てを持つことです。列挙型が定義済みのリストであることを考えると、それはあなたの消費するコードで古くなるべきではありません。

    あなたの関数が私の場合に予期しない値またはUndefinedを取得した場合の対処方法については、一般的な回答は不可能ではないようです。私にとっては、enum値を評価する理由のコンテキストに依存します。コード実行を停止する必要があるか、デフォルト値を使用できるかどうかです。

    3

    私の意見は、プログラマのエラーであるため、あなたはそれをアサートするか、RuntimException(Java、または他の言語に相当するものであれ)を投げてください。私はこれに使用するRuntimeExceptionから拡張された独自のUnhandledEnumExceptionを持っています。

    0

    有効な入力を提供することは呼び出し元関数の責任であり、暗黙的にenumにないものは無効です(実用的なプログラマはこれを暗に意味すると思われます)。つまり、これは、enumを変更するたびに、それを入力として受け入れるすべてのコード(および出力として出力するコード)を変更する必要があることを意味します。とにかくそれはおそらく本当です。頻繁に変更されるenumがある場合は、enumが通常はコンパイル時のエンティティであることを考慮して、enum以外のものを使用しているはずです。

    +0

    私は全く同意しますが、実世界では、列挙型が更新されたときにいくつかのメソッドが見落とされる可能性があります。そのため、私は質問しています。そのような場合はどうしますか? –

    3

    正確なプログラム応答はとなり、開発者は問題を容易に見つけ出すことができます。 mmyersJaredParはどちらも良い方法でした。

    なぜ死ぬのですか?それはとても極端なようです!

    あなたはを通じて適切かつちょうど秋列挙型の値を処理していない場合、あなたが予期しない状態にあなたのプログラムを入れていることであることの理由。いったん予期しない状態になったら、何が起こっているのかを知っている人。これは、悪いデータ、追跡が困難なエラー、またはセキュリティ上の脆弱性につながる可能性があります。

    また、プログラムが終了すると、QAでそれをキャッチしようとする可能性がさらに高くなります。

    +0

    はい、それは非常に極端なようです!アプリケーションがエラーから回復できるという意味で、例外をスローすることは十分ではないでしょうか?方法があまり重要でない場合はどうすればよいですか?失敗事例は、QAでキャプチャされますが、期待した結果とは異なる結果が生じる可能性があります。 –

    +0

    あなたはエラーから回復しますが、今はあなたのプログラムがどの状態にあるのか分かりません。私が述べたように、これは他の微妙な問題につながります。 QAはそれらの微妙なエラーを見て、報告しますが、問題の根本は解決されません。 –

    +0

    エラーから回復したのは私のものではなく、呼び出しの階層の上位層です。これはIMHOの重要な違いです。エラーがログに記録されている場合は、エラーメッセージが表示され、根本原因が直接発生します。 –

    0

    私は普通(0)未定義の値を定義しよう:

    enum Mood { Undefined = 0, Happy, Sad } 
    

    その方法は、私がいつも言うことができる:私は通常これを行う方法を

    switch (mood) 
    { 
        case Happy: Console.WriteLine("I am happy"); break; 
        case Sad: Console.WriteLine("I am sad"); break; 
        case Undefined: // handle undefined case 
        default: // at this point it is obvious that there is an unknown problem 
          // -> throw -> die :-) 
    } 
    

    これは、少なくともです。

    +0

    MSDNのどこかで、 "Undefined"値を列挙型として作成する必要があることは覚えていますが、このような値を持たない列挙型(FileModeなど)の場合は問題になります。それが必要でない限り、開発者はメソッドを書くたびにそれをチェックする必要があります。 –

    +1

    デフォルトのケースはまだ処理する必要があり、それが問題のポイントです。 –

    +0

    まあ、私はコメントで言及 - スローと死ぬ。 Exceptionクラスから派生した私自身の例外をスローします。 –

    1

    これはテスト駆動開発がなぜ重要かを証明する質問の1つです。

    この場合、文字通り値が処理されていないためサポートされていないため、NotSupportedExceptionに行きます。

    呼び出しコードでは、このような状況を処理できる必要があり、ユニットテストを作成してこのような状況を簡単にテストできます。

    +0

    これは良い点です。しかし、FileMode.WriteでStreamReaderを作成するときなど、有効な値に対してNotSupportedExceptionをスローすることがあります。私は、議論されている事件が他の事件のように扱われるべきではないほど十分に異なっていると考えていました。 –

    2

    C#の場合、知る価値のあるものはEnum.IsDefined() is dangerousです。あなたはあなたのようにそれに頼ることはできません。例外をスローして大声で死に至るのは、期待された値ではないものを得ることです。

    enumが整数ではないので、enumは整数ではないので、(enumが更新されずにswitch文が使用されていない限り)予期しない値を得ることができないため、Javaのenum 。あなたはまた、ヌル値に対応しなければなりません。しかし、あなたが認識していないnull以外の場合を得ることは例外をスローするための良いケースです。

    +0

    私は確かにC#にJavaの列挙型を好む。あなたが認識できない値の場合に投げ捨てる例外を教えてください。 –

    +0

    Javaでは、認識できなかった列挙型の値を取得した場合、IllegalStateExceptionをスローします。 IllegalArgumentExceptionはOKですが、私にとっては違法な状態のようです。 – cletus

    11

    私は上記の回答のほとんどが有効だと思いますが、私は正しいとは確信していません。

    正しい答えはあなたが非常にまれOO言語に切り替えていない、である、それはあなたがオブジェクト指向間違っをしているを示しています。この場合、Enumクラスに問題があることを示す完全な指標です。

    あなただけConsole.WriteLineを(mood.moodMessageを())を呼び出すと、状態のそれぞれについてmoodMessageを定義する必要があります。新しい状態が追加された場合

    - すべてのあなたのコードは自動的に適応させるべきで、何も、失敗しない例外をスローしたり、変更を必要とする必要があります。

    編集:コメントへの応答。あなたの例では

    は、「グッドOO」にFileModeオブジェクトによって制御されるファイルモードの機能があることを。これは、FileModeごとに異なる "オープン、リード、ライト..."操作を持つデリゲートオブジェクトを含むことができるので、File.open( "name"、FileMode.Create)は次のように実装できます。 API):

    open(String name, FileMode mode) { 
        // May throw an exception if, for instance, mode is Open and file doesn't exist 
        // May also create the file depending on Mode 
        FileHandle fh = mode.getHandle(name); 
        ... code to actually open fh here... 
        // Let Truncate and append do their special handling 
        mode.setPosition(fh); 
    } 
    

    これは、スイッチでそれをやろうとしているよりもはるかに滑らかな印象です...(ちなみに、この方法は、パッケージプライベートとおそらく「モード」のクラスに委譲両方だろう)

    OOがうまくいったら、すべての単一のメソッドは、数行の本当に分かりやすい単純なコード(TOO simple)のように見えます。私は答えを投稿しています

    +0

    他のすべてのものと同じように、列挙型は正しく使用されたときに良好です。たとえば、ファイル処理のためのAPIを設計している場合、FileModeが単純な値の列挙型で、動作がアタッチされていない場合は、 'File.Open(string filename、FileMode mode)'を作成します。私はこれを "OOを間違ってやっている"とは思わない。 –

    +2

    うわー、私はenumが悪いことについて何も言いませんでした!私はタイプセーフな列挙を愛しています - あなたが指定した使用は素晴らしいです。それは問題のスイッチステートメントです。これは、動作を列挙型に移動する必要があることを示します。 –

    +0

    私が誤解して申し訳ありませんが、質問はまだ残っています:あなたはswitch文なしで 'File.Open'をどのように実装しますか?自分自身について言えば、FileModeオブジェクトにはファイルを開くメソッドを与えません。なぜなら、単にそこに属していないからです。代わりに何をお勧めしますか? –

    関連する問題