無効なジェネリック型引数の最良の例外


106

私は現在、列挙型を処理するためのジェネリックメソッドを持つUnconstrainedMelodyのコードを書いています。

これで、「フラグ」列挙型でのみ使用することを目的とした一連のメソッドを持つ静的クラスができました。これを制約として追加することはできません...したがって、他の列挙型でも呼び出される可能性があります。その場合、例外をスローしたいのですが、どれをスローするかわかりません。

これを具体的にするために、私がこのようなものを持っている場合:

// Returns a value with all bits set by any values
public static T GetBitMask<T>() where T : struct, IEnumConstraint
{
    if (!IsFlags<T>()) // This method doesn't throw
    {
        throw new ???
    }
    // Normal work here
}

スローするのに最適な例外は何ですか?ArgumentException論理的に聞こえますが、これは通常の引数ではなく引数であり、簡単に混乱する可能性があります。自分のTypeArgumentExceptionクラスを紹介する必要がありますか?使用しInvalidOperationExceptionますか?NotSupportedException?他に何か?

私はと思い、むしろそれが明確に行うには正しいことだがない限り、このために私自身の例外を作成していません。


今日は、制約で説明できない使用中の型に追加の要件が課されるジェネリックメソッドを作成するときに、これに遭遇しました。既にBCLに例外の種類が見つからないことに驚きました。しかし、この正確なジレンマは、Flags属性でのみ機能するジェネリックの同じプロジェクトで数日前に直面したものです。不気味な!
Andras Zoltan 2012年

回答:


46

NotSupportedException それは明白に適合しているように聞こえますが、ドキュメントはそれを別の目的に使用する必要があることを明確に述べています。MSDNクラスのコメントから:

基本クラスでサポートされていないメソッドがあり、これらのメソッドは派生クラスで実装されることが期待されています。派生クラスは、基本クラスのメソッドのサブセットのみを実装し、サポートされていないメソッドに対してNotSupportedExceptionをスローする場合があります。

もちろん、NotSupportedException特にその常識的な意味を考えると、明らかに十分な方法があります。そうは言っても、それが適切かどうかはわかりません。

無制限のメロディーの目的を考えると...

"T:enum"または "T:delegate"の型制約があるジェネリックメソッド/クラスで実行できるさまざまな便利なものがありますが、残念ながらC#では禁止されています。

このユーティリティライブラリは、ildasm / ilasmを使用して禁止事項を回避します...

... Exceptionカスタムを作成する前に満たす必要のある証明の負担が大きいにもかかわらず、新しいものを準備する必要があるようExceptionsです。のようなものInvalidTypeParameterExceptionがライブラリ全体で役に立つかもしれません(そうでないかもしれません-これは確かにエッジケースですよね?)

クライアントは、これをBCL例外と区別できる必要がありますか?クライアントが誤ってこれをバニラを使用して呼び出す可能性があるのはいつenumですか?カスタム例外クラスを作成する際に考慮すべき要素に対する承認された回答によって提示される質問にどのように答えますか?


実際、コードコントラクトと同じように、そもそも内部専用の例外をスローするのはほとんど魅力的です...誰かそれをキャッチする必要があるとは思いません。
ジョンスキート

残念ながら、nullを返すことはできません。
Jeff Sternal

25
TypeArgumentExceptionを使用します。
Jon Skeet、

フレームワークに例外を追加すると、「証明の負担」が高くなる可能性がありますが、カスタム例外を定義することはできません。物事が好きInvalidOperationException「fooがすでに存在していることを何かを追加するために、コレクションのバーを尋ね、そのバーはIOEをスロー」と「fooが何かを追加するために、コレクションのバーを尋ね、そのバーは、バーがそれが期待されていない場合でも、IOEをスローしたボズを呼び出します」ので、不快ですどちらも同じ例外タイプをスローします。最初のものをキャッチすることを期待しているコードは、後者を期待していません。言われていること
スーパーキャット2013年

...フレームワークの例外を支持する議論は、カスタムの例外よりも説得力があると思います。NSEの一般的な性質は、オブジェクトへの参照が一般的なタイプであり、参照が指す特定のタイプのオブジェクトのすべてではなく一部が機能をサポートする場合、特定のタイプで機能を使用しようとすると、 NSEをスローする必要があることをサポートしていません。私はFoo<T>、「一般的なタイプ」でありFoo<Bar>、そのコンテキストでは「特定のタイプ」であると見なしますが、それらの間には「継承」関係はありません。
スーパーキャット2013年

24

NotSupportedExceptionは回避します。この例外は、メソッドが実装されておらず、このタイプの操作がサポートされていないことを示すプロパティがあるフレームワークで使用されます。ここには収まりません

InvalidOperationExceptionは、ここでスローできる最も適切な例外だと思います。


NSEについてのヘッドアップをありがとう。ところで、あまりにも同僚からでしょう歓迎入力、...
ジョンスキート

要点は、Jonが必要とする機能はBCLと似ていないことです。コンパイラはそれをキャッチすることになっています。NotSupportedExceptionから "property"要件を削除すると、言及したもの(ReadOnlyコレクションなど)がJonの問題に最も近いものになります。
Mehrdad Afshari、

ワンポイント-私はあるIsFlags方法(それは一般的なことする方法である必要があります)なければならないのソート操作のこのタイプがサポートされていないことを示すの...ので、その意味でNSEが適切であろうが。つまり、発信者最初に確認できます。
Jon Skeet、

@ジョン:そのようなプロパティなくても、タイプのすべてのメンバーは本質的にで装飾されているという事実に依存していると思いますTが、NSEをスローすることは有効です。enumFlags
Mehrdad Afshari、

1
@ジョン:StupidClrException面白い名前を付けます;)
Mehrdad Afshari

13

ジェネリックプログラミングでは、無効な型パラメーターが実行時にスローされません。コンパイルしないでください。コンパイル時の強制が必要です。何IsFlag<T>()が含まれているのかはわかりませんが、「フラグ」でしか作成できない型を作成しようとするなど、これをコンパイル時の強制に変えることができます。おそらくtraitsクラスが役立つでしょう。

更新

スローする必要がある場合は、InvalidOperationExceptionに投票します。その理由は、ジェネリック型にはパラメーターがあり、(メソッド)パラメーターに関連するエラーはArgumentException階層に集中しているためです。ただし、ArgumentExceptionに関する推奨事項では、

失敗に引数自体が含まれない場合は、InvalidOperationExceptionを使用する必要があります。

そこでの信仰の少なくとも一つの飛躍があり、そのメソッドのパラメータの推奨事項は、に適用されることもあり、一般的なパラメータが、のSystemException hierachyの私見では、より良いものはありません。


1
いいえ、これをコンパイル時に制限できる方法はありません。IsFlag<T>enumが[FlagsAttribute]適用されているかどうかを判別し、CLRには属性に基づく制約がありません。それは完璧な世界にあるでしょう-またはそれを制約する他の方法があるでしょう-しかしこの場合それはうまくいきません:(
Jon Skeet

(ただし、一般的な原則としては+1- それを制限できるようになりたいです。)
Jon Skeet

9

それはあなたが言っていることなので、私はNotSupportedExceptionを使用します。特定の列挙型以外の列挙型はサポートされいません。もちろん、これは例外メッセージでより明確に述べられます。


2
NotSupportedExceptionは、BCLで非常に異なる目的に使用されます。ここには収まりません。 blogs.msdn.com/jaredpar/archive/2008/12/12/...
JaredPar

8

私は一緒に行きNotSupportedExceptionます。一方でArgumentExceptionルックスの罰金メソッドに渡された引数が受け入れられないとき、それは本当に期待されます。型引数は、実際の「引数」ではなく、呼び出す実際のメソッドを定義する特性です。InvalidOperationException実行している操作が有効である場合にスローされますが、特定の状況では受け入れられません。

NotSupportedException操作が本質的にサポートされていない場合にスローされます。たとえば、特定のメンバーがクラスにとって意味をなさないインターフェースを実装する場合。これは同様の状況のようです。


うーん。それはまだありませんかなり右に感じるが、私はそれに近いものになるだろうと思います。
Jon Skeet、

ジョン:コンパイラーに捕まってもらうのは当然だと思っているので、それは正しくありません。
Mehrdad Afshari、

うん。これは私が適用したい奇妙な種類の制約ですが、適用できません:)
Jon Skeet

6

明らかに、Microsoftは、ExceptionsセクションArgumentExceptionExpression.Lambda <>Enum.TryParse <>またはMarshal.GetDelegateForFunctionPointer <>の例で示されているように、これを使用しています。私は、どちらか(ローカル基準ソースを検索するにもかかわらず、それ以外の場合を示す任意の例を見つけることができなかったTDelegateTEnum)。

したがって、少なくともMicrosoftのコードではArgumentException、基本的な変数の引数のほかに、無効なジェネリック型の引数を使用するのが一般的な習慣であると想定しても安全だと思います。ドキュメント内の例外の説明はそれらを区別しないので、それはあまりにも多くのストレッチではありません。

うまくいけば、それは問題の事柄をすべて一度に決定します。


フレームワーク内の1つの例では十分ではありません。それは、MSが他のケースで不適切な選択を行ったと考える場所の数を考えると、型引数が通常ではないため、TypeArgumentExceptionから派生することArgumentExceptionはありません。引数。
Jon Skeet、2016

1
「MSが一貫して行うこと」という点では、これは確かにより強力です。ドキュメントの照合という点で、これ以上説得力のあるものにはなりません...そして、C#チームには、通常の引数と型引数の違いを深く気にする人がたくさんいることを知っています:)でも、例をありがとう-彼らはとても役に立ちます。
Jon Skeet、2016

@Jon Skeet:編集しました。現在は、異なるMSライブラリからの3つの例が含まれており、すべてArgumentExceptionがスローされたものとして文書化されています。したがって、それが不適切な選択である場合、少なくともそれは一貫した不適切な選択です。;)Microsoftは、通常の引数と型引数は両方とも引数であると想定しています。個人的には、そのような仮定はかなり合理的だと思います。^^ '
アリス

ああ、気にしないで、あなたはすでにそれに気づいたようです。お役に立てて嬉しいです。^^
アリス

私はそれらを同じように扱うことが合理的であるかどうかについて意見の相違に同意する必要があると思います。リフレクションや言語ルールなどに関しては、確かに同じではありません。扱い方は大きく異なります。
Jon Skeet、2016


2

カスタムメイドの例外のスローは、疑わしい場合は常に行う必要があります。APIユーザーのニーズに関係なく、カスタム例外は常に機能します。開発者が気にしない場合は、どちらの例外タイプもキャッチできますが、開発者が特別な処理を必要とする場合は、SOLになります。


また、開発者は、スローされたすべての例外をXMLコメントに文書化する必要があります。
エリックシュナイダー、

1

NotSupportedExceptionから継承するのはどうですか。@Mehrdadが最も理にかなっていることに同意しますが、完全に適合していないように見えるというあなたの意見を聞いています。そのため、NotSupportedExceptionを継承します。そのようにして、APIをコーディングする人々は、NotSupportedExceptionをキャッチできます。


1

私は常にカスタム例外を書くことに常に警戒しており、純粋にそれらが常に明確に文書化されているわけではないという理由で、正しく名前が付けられていないと混乱を引き起こします。

この場合、フラグチェックの失敗に対してArgumentExceptionをスローします。それは本当に好み次第です。私が見たいくつかのコーディング標準は、このようなシナリオでスローされる例外のタイプを定義することまで行っています。

ユーザーが列挙型ではない何かを渡そうとすると、InvalidOperationExceptionがスローされます。

編集:

他のものはこれがサポートされていないという興味深い点を提起します。NotSupportedExceptionに関する私の唯一の懸念は、「ダークマター」がシステムに導入されたときにスローされる例外、または言い換えると、「このメソッドはこのインターフェイスでシステムに入る必要がありますが、バージョン2.4になるまでオンにしないでください」

NotSupportedExceptionsがライセンス例外としてスローされることも確認しました。「このソフトウェアの無料バ​​ージョンを実行しているため、この機能はサポートされていません」。

編集2:

別の可能なもの:

System.ComponentModel.InvalidEnumArgumentException  

列挙子である無効な引数を使用するとスローされる例外。


私はそれを列挙型になるように制限します(ちょっとした冗談の後で)-それは私が心配しているフラグだけです。
Jon Skeet、

これらのライセンス関係者は、LicensingExceptionから継承するクラスのインスタンスをスローする必要があると思いますInvalidOperationException
Mehrdad Afshari、

Mehrdad氏も同意します。残念ながら、例外はフレームワークにグレーが多く含まれている領域の1つです。しかし、これは多くの言語で同じだと確信しています。(vb6のランタイムエラー13 heheに戻ると言っているわけではありません)
Peter

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.