11

編集1

列挙型がメソッドの引数ではないように更新されました...

質問

このタイプの問題は、switchステートメントの列挙型でよく発生します。サンプルコードでは、開発者はプログラムが現在使用しているすべての国を考慮していますが、国の列挙に別の国が追加された場合は、例外がスローされます。私の質問は、どのタイプの例外をスローする必要があるかということです。

サンプルコード:

enum Country
{
    UnitedStates, Mexico,
}

public string GetCallingCode(Guid countryId){
    var country = GetCountry(countryId);
    switch (country)
    {
        case Country.UnitedStates:
            return "1";
            break;
        case Country.Mexico:
            return "52";
            break;
        default:
            // What to throw here
        break;
    }
}

私は見ました

  • NotImplemented要求されたメソッドまたは操作が実装されていない場合にスローされる例外。
  • NotSupported これらのメソッドが代わりに派生クラスに実装されることを期待して、基本クラスでサポートされていないメソッドがあります。派生クラスは、基本クラスのメソッドのサブセットのみを実装し、サポートされていないメソッドに対してNotSupportedExceptionをスローする場合があります。
    オブジェクトが要求された操作を実行できる場合があり、オブジェクトの状態によって操作を実行できるかどうかが決まるシナリオについては、InvalidOperationExceptionを参照してください。
  • InvalidOperation は、無効な引数以外の理由でメソッドの呼び出しに失敗した場合に使用されます。

私の推測では、NotImplementedまたはInvalidOperationのいずれかです。どちらを使うべきですか?誰かがより良いオプションを持っていますか(私はあなた自身を転がすことは常にオプションであることを知っています)

4

6 に答える 6

12

ArgumentExceptionアグリメントが無効なので、私は一緒に行きます。

編集:http://msdn.microsoft.com/en-us/library/system.argumentexception%28v=vs.71%29.aspx

InvalidEnumArgumentException問題をより正確に説明しているかもしれないもありますが、私はこれまで誰もそれを使用したことがありません。

于 2012-10-16T14:07:08.497 に答える
4

1つのオプションは、デバッグモードでほぼメソッドコントラクトチェックを実行することです。見栄えの良いフォームの拡張メソッドをスローします。

[Conditional("DEBUG")]
public static bool AssertIsValid(this System.Enum value)
{
    if (!System.Enum.IsDefined(value.GetType(), value))
        throw new EnumerationValueNotSupportedException(value.GetType(), value); //custom exception
}

おそらくデバッグモードでしか持っていないので、開発/テスト環境と単体テストに合格し、本番環境ではオーバーヘッドはありません(それはあなた次第ですが)

public string GetCallingCode(Guid countryId)
{
    var country = GetCountry(countryId);
    country.AssertIsValid(); //throws if the country is not defined
    
    switch (country)
    {
        case Country.UnitedStates:
            return "1";
        case Country.Mexico:
            return "52";
    }
}

これは実際にはあなたのGetCountry方法の責任ですが、私は提案します。が無効であることを認識しcountryId、例外をスローする必要があります。

とにかく、これはあなたのユニットテストによっても実際に捕らえられるべきです、あるいはどういうわけかよりよく扱われるべきです。string / intを列挙型に変換する場合は常に、単一のメソッドで処理する必要があります。このメソッドは、(他のParseメソッドと同じように)チェック/スローでき、すべての有効な数値をチェックする単体テストを実行できます。

ArgumentExceptions一般に、いくつかの条件(引数なし)の場合があるため、さまざまな(など)は適切な候補ではないと思います。チェックコードを1つの場所に移動すると、聞いている開発者と正確に通信する独自​​の例外をスローすることもできます。

編集:議論を考えると、ここには2つの特定のケースがあると思います。

ケース1:基になる型を同等の列挙型に変換する

メソッドが何らかの入力データ(string、int、Guid?)を受け取る場合、列挙型への変換を実行するコードは、使用可能な実際の列挙型があることを検証する必要があります。これは私が私の答えで上に投稿したケースです。そのような場合、おそらくあなた自身の例外を投げるか、あるいはInvalidEnumArgumentException

これは、標準の入力検証とほとんど同じように扱う必要があります。システムのどこかでガベージインを提供しているので、他の解析メカニズムと同じように処理します。

var country = GetCountry(countryId);

switch (country)
{
    case Country.UnitedStates:
        return "1";
    case Country.Mexico:
        return "52";
}

private Country GetCountry(Guid countryId)
{
    //get country by ID
    
    if (couldNotFindCountry)
        throw new EnumerationValueNotSupportedException(.... // or InvalidEnumArgumentException

    return parsedCountry;
}

編集:もちろん、コンパイラーはメソッドがスロー/リターンすることを要求するので、ここで何をすべきかよくわかりません。それはあなた次第だと思います。それが実際に発生する場合は、入力検証に合格したものの、新しい値を処理するようにスイッチ/ケースを更新しthrow new BoneheadedException()いないため、おそらく骨の折れる例外です(以下のケース2) 。

ケース2:コード内のスイッチ/ケースブロックによって処理されない新しい列挙値を追加する

あなたがあなたのコードの所有者である場合、これは@NominSimの回答でEricLippertによって説明された「骨頭」の例外に該当します。ただし、プログラムを例外/無効な状態のままにしておくと、実際には例外が発生することはありません。

これに最適なのは、列挙型に対してスイッチ/ケース(または同様のもの)を実行する場所である可能性が高いため、列挙型の定義されたすべての値に対してメソッドを自動的に実行する単体テストの作成を検討する必要があります。したがって、怠惰であるか、誤ってブロックを見逃した場合、単体テストは、列挙リストの変更を説明するためにメソッドを更新しなかったことを警告します。

最後に、列挙型が値を更新したことに気づかなかったサードパーティからのものである場合は、すべての期待値を検証する簡単な単体テストを作成する必要があります。したがって、とのチェックを使用してプログラムを作成した場合UnitedStatesMexico単体テストはそれらの値のスイッチ/ケースブロックであり、例外をスローする必要があります。そうしないと、追加された場合に警告が表示されますCanada。サードパーティのライブラリを更新した後にそのテストが失敗した場合、互換性を保つために何をどこで変更する必要があるかがわかります。

したがって、この「ケース2」では、必要な古い例外をスローする必要があります。これは、ユニットテストのユーザーまたはコンシューマーに正確に欠落しているものを正確に伝達する限り、ユニットテストによって処理されるためです。


どちらの場合でも、スイッチ/ケースコードは無効な入力をあまり気にせず、そこで例外をスローしないようにする必要があると思います。これらは、設計時に(単体テストを介して)スローするか、入力を検証/解析するときにスローする必要があります(したがって、適切な「解析/検証」例外をスローします)。


編集:Eric Lippertからの投稿に出くわし、C#コンパイラが、戻り値のあるメソッドが戻りなしで「エンドポイント」に到達したかどうかを検出する方法について説明しました。コンパイラーは、エンドポイントに到達できない場合があることを保証するのに優れていますが、上記の場合、開発者はエンドポイントに到達できないことを知っています(上記の状況が関係する場合を除くBoneheadedExceptions)。

これらのケースを解決するために開発者として何をすべきか(少なくとも私が見たもの)は議論されていませんでした。コンパイラー、戻り値を提供するか、例外がそのコードに到達しないことがわかっている場合でも例外をスローすることを要求します。グーグルはこの場合に活用するためのいくつかの例外を魔法のように表面化していません(私は良い検索用語を見つけることができませんでしたが)、そして私はむしろ例外を投げて、それが終わりに到達できないという私の仮定が間違っているというよりも問題を通知しない、または望ましくない動作を引き起こす可能性のある値を返す。たぶんUnexpectedCodePathFailedToReturnValueException、この場合、ある種のいくつかが最も有効でしょう。時間があれば、もう少し掘り下げて、プログラマーに質問を投稿します。いくつかの議論を集めるために。

于 2012-10-16T14:30:59.747 に答える
1

あなたがリストした例外のうち、InvalidOperationExceptionあなたのシナリオにのみ適合します。あなたの値は引数として提供されるので、私はこれ、またはどちらか、ArgumentExceptionまたはより具体的なものを使用することを検討します。ArgumentOutOfRangeExceptionswitch

または、あなたが言うように、あなた自身を転がしてください。

編集:更新された質問に基づいてInvalidOperationException、フレームワークの例外を使用するかどうかをお勧めします。ただし、このより一般的なケースでは、私は間違いなく自分自身をロールバックしたいと思います-InvalidOperationExceptionコールスタックの他の場所(おそらくフレームワーク自体によって!)でキャッチされないことを保証することはできませんので、独自の例外タイプを使用する方がはるかに多くなります壮健。

于 2012-10-16T14:08:56.717 に答える
1

使用InvalidOperationExceptionしている値が純粋にオブジェクトの現在の状態の積である場合に使用します。それが言うように:

メソッド呼び出しがオブジェクトの現在の状態に対して無効である場合にスローされる例外。

更新された質問でも、適切に処理できない特定の値は、渡された引数から派生したものであるため、引き続き使用します-引数から派生した情報が一致しないArgumentExceptionことをエラーメッセージで説明できますあなたが扱うことができるものは何でも。


両方にとって、NotImplementedExceptionそしてNotSupportedException期待は、発信者が何をしても、彼らは状況を改善することができないだろうということです。一方ArgumentException、およびInvalidOperationExceptionは、呼び出し元が別の引数を使用するか、オブジェクトを別の状態に(それぞれ)遷移させる場合、呼び出しが機能する可能性があるという手がかりです。

于 2012-10-16T14:33:37.007 に答える
-1

個人的には、これは例外の適切な場所ではないと思います。国を追加する場合は、switchステートメントにケースを追加する必要があります。列挙型に値を追加するため、コードが壊れないようにする必要があります。

Eric Lippertによる例外をいつ使用するかについての記事があり、探している例外のタイプを次のように分類しています:(私のものではないという言い回しを許してください)

骨頭の例外はあなた自身のひどい欠点です、あなたはそれらを防ぐことができたかもしれません、そしてそれ故にそれらはあなたのコードのバグですそれらを捕まえるべきではありません。そうすることで、コードのバグを隠すことができます。むしろ、そもそも例外が発生しないようにコードを記述して、キャッチする必要がないようにする必要があります。

于 2012-10-16T14:10:58.323 に答える
-2

列挙型は可能な値を処理する値に制限するため、別の値を渡すことはできません。したがって、例外は必要ありません。

于 2012-10-16T14:08:35.580 に答える