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によって説明された「骨頭」の例外に該当します。ただし、プログラムを例外/無効な状態のままにしておくと、実際には例外が発生することはありません。
これに最適なのは、列挙型に対してスイッチ/ケース(または同様のもの)を実行する場所である可能性が高いため、列挙型の定義されたすべての値に対してメソッドを自動的に実行する単体テストの作成を検討する必要があります。したがって、怠惰であるか、誤ってブロックを見逃した場合、単体テストは、列挙リストの変更を説明するためにメソッドを更新しなかったことを警告します。
最後に、列挙型が値を更新したことに気づかなかったサードパーティからのものである場合は、すべての期待値を検証する簡単な単体テストを作成する必要があります。したがって、とのチェックを使用してプログラムを作成した場合UnitedStates
、Mexico
単体テストはそれらの値のスイッチ/ケースブロックであり、例外をスローする必要があります。そうしないと、追加された場合に警告が表示されますCanada
。サードパーティのライブラリを更新した後にそのテストが失敗した場合、互換性を保つために何をどこで変更する必要があるかがわかります。
したがって、この「ケース2」では、必要な古い例外をスローする必要があります。これは、ユニットテストのユーザーまたはコンシューマーに正確に欠落しているものを正確に伝達する限り、ユニットテストによって処理されるためです。
どちらの場合でも、スイッチ/ケースコードは無効な入力をあまり気にせず、そこで例外をスローしないようにする必要があると思います。これらは、設計時に(単体テストを介して)スローするか、入力を検証/解析するときにスローする必要があります(したがって、適切な「解析/検証」例外をスローします)。
編集:Eric Lippertからの投稿に出くわし、C#コンパイラが、戻り値のあるメソッドが戻りなしで「エンドポイント」に到達したかどうかを検出する方法について説明しました。コンパイラーは、エンドポイントに到達できない場合があることを保証するのに優れていますが、上記の場合、開発者はエンドポイントに到達できないことを知っています(上記の状況が関係する場合を除くBoneheadedExceptions
)。
これらのケースを解決するために開発者として何をすべきか(少なくとも私が見たもの)は議論されていませんでした。コンパイラーは、戻り値を提供するか、例外がそのコードに到達しないことがわかっている場合でも例外をスローすることを要求します。グーグルはこの場合に活用するためのいくつかの例外を魔法のように表面化していません(私は良い検索用語を見つけることができませんでしたが)、そして私はむしろ例外を投げて、それが終わりに到達できないという私の仮定が間違っているというよりも問題を通知しない、または望ましくない動作を引き起こす可能性のある値を返す。たぶんUnexpectedCodePathFailedToReturnValueException
、この場合、ある種のいくつかが最も有効でしょう。時間があれば、もう少し掘り下げて、プログラマーに質問を投稿します。いくつかの議論を集めるために。