いつ例外をスローするのですか?


435

私のアプリケーションが予期しないすべての条件に対して作成された例外があります。 UserNameNotValidExceptionPasswordNotCorrectExceptionなど

ただし、これらの条件の例外を作成するべきではないと言われました。私のUMLでは、これらはメインフローの例外です。では、なぜそれが例外ではないのですか?

例外を作成するためのガイダンスまたはベストプラクティスはありますか?


58
もう一度開いてください。これは非常に賢明で有効な質問です。質問にはある程度の意見が含まれますが、この場合は「ベストプラクティス」の問題だと思います。
Tim Long

19
+1で再開。他にも多くの興味深いトピックが「依存する」ため、意思決定の際にトレードオフを分析することは非常に役立ちます。人々が答えの中で事実と意見を混同するという事実はこれを否定するものではありません。泥をふるいにかけることは、読者に任せるべき運動です。
aron

12
また、この質問はベストプラクティスに関連しているため、再開する必要があることにも同意します。ちなみに、ベストプラクティスは常に他者を助けることができる意見です。
Ajay Sharma

6
Microsoftは次のように述べています。「エラーコードを返さないでください。例外は、フレームワークでエラーを報告する主な手段です。」および「...メンバーが意図したとおりに実行できない場合、それは実行エラーと見なされ、例外がスローされます。」msdn.microsoft.com/library/ms229030%28v=vs.100%29.aspx
Matsen75

4
これらは完全に賢明な例外かもしれませんが、どのメソッドがそれらをスローするかに依存しています。IsCredentialsValid(username,password)ユーザー名またはパスワードが無効な場合、呼び出されたメソッドは例外をスローしませんが、falseを返します。しかし、認証が失敗した場合、データベースからデータを読み取るメソッドがそのような例外を合法的にスローする可能性があると言います。つまり、メソッドが実行すべきタスクを実行できない場合は、例外をスローする必要があります。
JacquesB 2016

回答:


629

私の個人的なガイドラインは次のとおりです。現在のコードブロックの基本的な仮定が偽であることが判明した場合、例外がスローされます。

例1:任意のクラスを検査し、そのクラスがList <>から継承する場合にtrueを返す関数があるとします。この関数は、「このオブジェクトはリストの子孫ですか?」という質問をします。この関数は、操作に灰色の領域がないため、例外をスローすることはありません。すべてのクラスはList <>から継承するか、継承しないため、答えは常に「はい」または「いいえ」です。

例2:List <>を調べて、長さが50を超える場合はtrueを返し、長さが短い場合はfalseを返す別の関数があるとします。この関数は、「このリストには50を超える項目がありますか?」という質問をします。しかし、この質問は仮定をします-与えられるオブジェクトはリストであると仮定します。NULLを渡すと、その仮定は誤りになります。その場合には、関数が戻った場合のいずれか偽、それは独自のルールを破っています。関数は何も返さ、質問に正しく回答したと主張することはできません。そのため、戻りません-例外をスローします。

これは、「読み込まれた質問」の論理的誤りに相当します。すべての関数が質問をします。与えられた入力がその質問を誤りである場合は、例外をスローします。この行は、voidを返す関数で描くのは困難ですが、重要な点は、入力に関する関数の仮定に違反すると、通常に戻るのではなく、例外をスローする必要があることです。

この方程式の反対側は、関数が例外を頻繁にスローすることがわかった場合、おそらくそれらの仮定を調整する必要があるでしょう。


15
丁度!関数の前提条件(引数に関する仮定)が壊れている場合にのみ、例外がスローされます。
ライトマン2015年

11
言語学では、これは前提の失敗と呼ばれることがあります。古典的な例はベルトランラッセルによるものです。「フランスの王はハゲですか」は「はい」でも「いいえ」でも答えられません(「フランスの王はハゲである」は真実でも偽でもありません)。つまり、フランスの王がいるということです。前提の失敗は明確な説明でよく見られますが、これはプログラミング時によく見られます。たとえば、「リストの先頭」は、リストが空の場合の前提エラーであり、例外をスローすることが適切です。
Mohan

これはおそらく最良の説明です!
gaurav

ありがとうございました。そう。たくさん。「フランスの王はハゲです」。メイノンのジャングルを研究しているときにこれを聞いたことがあります。@Mohan
ErlichBachman

285

彼らは普通に起こることだからです。例外はフロー制御メカニズムではありません。ユーザーはパスワードを間違えることがよくあります。これは例外的なケースではありません。例外は本当にまれなことであり、UserHasDiedAtKeyboardタイプの状況です。


3
うーん、違う。最大のパフォーマンスが必要ない場合は、例外を制御フローメカニズムとして使用できます。これは、ほとんどのWebアプリに当てはまります。Pythonは例外 'StopIteration'を使用してイテレーターを終了しますが、非常にうまく機能します。コスト等IOに比べて何もありません
Seun Osewa

9
+1の正解。私はAPIに取り組んでいる開発者にとても不満を感じています。本当に例外が必要なケースはほとんどありません。25種類の例外が定義されている場合は、設計をもう一度見てください。間違っている可能性があります。
2011年

1
たとえば、StackOverflowで他の誰かの投稿を削除するなど、ユーザーがWebページのコードを操作して許可されていない違法なアクションを試みたときに例外が発生する必要がありますか?
Rajat Gupta

30
例外は制御フローメカニズムです。あなたはそれらを投げることができます。あなたはそれらをキャッチすることができます。コントロールを別のコードに移動しました。それが制御フローです。言語に例外がある唯一の理由は、「あれはただ失敗したのか?」あなたがするすべての後。たとえば、Haskellには例外がありません。モナドとdo記法がエラーチェックを自動化できるからです。
ジェシー

2
例外は、制御フローメカニズム以上のものです。それらは(メソッド)クライアントに、彼らが認識して処理なければならない例外的な結果に関する有用な情報を提供します。つまり、適切に使用すると、例外によりAPIがより堅牢になります
idelvall

67

私の小さなガイドラインは、素晴らしい本「コードコンプリート」に大きく影響されています。

  • 例外を使用して、無視してはならないことを通知します。
  • エラーをローカルで処理できる場合は、例外を使用しないでください
  • 例外がルーチンの残りの部分と同じ抽象化レベルであることを確認してください。
  • 例外は、本当に例外的なもののために予約する必要があります。

35

ユーザー名が有効でない場合やパスワードが正しくない場合も例外ではありません。これらは、通常の操作の流れで期待すべきことです。例外とは、通常のプログラム操作の一部ではなく、まれなことです。

編集:メソッドを呼び出すだけではメソッドが例外をスローするかどうかを判断できないため、例外の使用は好きではありません。そのため、適切な方法で状況を処理できない場合にのみ例外を使用する必要があります(「メモリ不足」または「コンピュータが燃えている」と考えてください)。


「呼び出しを見るだけではメソッドが例外をスローするかどうかがわからないため、例外を使用するのは好きではありません。」これが、例外をサポートする言語にチェック例外がある理由です。
2009

1
チェック例外には、独自の一連の問題があります。通常のワークフローの一部ではなく、「例外的な状況」の例外を使用したいのですが。
EricSchaefer、2009

2
あなたの編集に応じて。概要セクションの最後に常に関数のスローする例外をxmlドキュメントに入れて、その情報をインテリセンスで確認できるようにしています。
Matthew Vines、

1
たとえば、StackOverflowで他の誰かの投稿を削除するなど、ユーザーがWebページのコードを操作して許可されていない違法なアクションを試みたときに例外が発生する必要がありますか?
Rajat Gupta

1
エラー(メモリ、コンピュータの起動)の処理とコード例外(レコードの欠落、無効な入力タイプなど)の処理について話しているようです。両者には明確な違いがあると思います。
チャックバージェス

28

経験則の1つは、通常は予測できなかった場合に例外を使用することです。例としては、データベース接続、ディスク上のファイルの欠落などがあります。たとえば、ユーザーが不正なパスワードでログインしようとした場合、ブール値を返し、状況を適切に処理する方法を知っている関数を使用する必要があります。誰かがパスワードを誤って入力したという理由だけで例外をスローして、実行を突然終了したくない場合。


6
例外でプログラムの実行を停止する必要はありません...例外をスローすると、呼び出し元は例外をキャッチし、可能であればそれを処理して、ログとエラーを記録し、次に進みます。そこにそれが発生した場所、それをキャッチし、それとの契約-それはちょうど、コールスタックまで例外をスロー保つために「悪いフォーム」です
Krakkos

2
しかし、直接処理できるのであれば、なぜそれらをスローするのか。パスワードが間違っている場合、または何かが間違っている場合は、falseを返してエラーを出力します
My1

ディスク上のファイルがありません」ほとんどの言語フレームワーク(.NETフレームワークなど)は、ファイルの存在を確認するためのAPIも提供しています。ファイルに直接アクセスする前に、それらを使用しないでください。
user1451111

23

他の人は、ユーザーがタイプミスした場合、通常のフローでは不正なログインが予想されるため、例外を使用しないことを提案しています。私は同意しません、そして私には推論がありません。それをファイルを開くことと比較してください。ファイルが存在しないか、何らかの理由で利用できない場合、フレームワークによって例外がスローされます。上記のロジックを使用することは、Microsoftの誤りでした。彼らはエラーコードを返すべきでした。解析、Webリクエストなども同様です。

私は通常のフローの悪いログイン部分を考慮しません、それは例外的です。通常、ユーザーは正しいパスワードを入力し、ファイルは存在します。例外的なケースは例外的であり、それらの例外を使用することは完全に問題ありません。戻り値をスタックのnレベルまで伝播することによってコードを複雑にすることは、エネルギーの浪費であり、コードが乱雑になります。機能する可能性のある最も単純なことを行います。エラーコードを使用して時期尚早に最適化しないでください。例外的に例外的に発生することはめったになく、例外をスローしない限り、例外によるコストはかかりません。


ただし、openを呼び出す前にファイルが存在することを確認できます(もちろんフレームワークによって異なります)。そのため、そのファシリティが存在するため、チェックとファイルを開こうとする間にファイルが消失した場合は例外です。
blowdart 2008

7
ファイルが存在しても、たとえば、ユーザーがファイルへの書き込みを許可されているわけではありません。考えられるすべての問題をチェックすることは、非常に面倒でエラーが発生しやすくなります。+コードを複製しています(DRY)。
ビョルンレッペン2008

無効なパスワードの例外の1つの点は、ユーザーがパスワードを入力するときに、リターンコードソリューションと比較した場合の遅延が認識されないことです。
paperhorse 2008

7
「戻り値をスタックのnレベルまで伝播することによってコードを複雑にすることは、エネルギーの浪費であり、コードが乱雑になります。」私にとって、これは例外を使用する非常に強力な理由です。優れたコードは通常、小さな関数で構成されています。そのエラーコードをある小さな関数から別の小さな関数に何度も渡してはなりません。
ベルチン

混乱は、おそらくlogin-typeメソッドの予測可能な結果がパスワードが正しくない可能性があり、実際にはこれを判別するために使用され、この場合は例外がありたくないという仮定から生じたと思います。一方、file openタイプシナリオでは、特定の結果が望まれます。不正な入力パラメーターまたは何らかの外部要因のためにシステムが結果を提供できない場合、それは例外の完全に論理的な使用です。
theMayer 2017年

17

例外はややコストのかかる効果です。たとえば、無効なパスワードを提供するユーザーがいる場合、通常は失敗フラグまたは無効であることを示すその他のインジケーターを返すことをお勧めします。

これは、例外の処理方法、真の不正な入力、および固有の重要な停止項目は例外であるべきですが、失敗したログイン情報ではありません。


14

現在の状態から抜け出すためにあなたにできることが何もないときだけ、あなたは例外を投げるべきだと思います。たとえば、メモリを割り当てていて、割り当てるものがない場合などです。あなたが言及したケースでは、それらの状態から明確に回復でき、それに応じて呼び出し元にエラーコードを返すことができます。


この質問への回答を含め、「例外的な」状況でのみ例外をスローすべきであるという多くのアドバイスが表示されます。これは表面上は合理的に思えますが、ある質問(「例外をスローする必要がある場合」)を別の主観的な質問(「例外的なもの」)に置き換えるため、欠点のあるアドバイスです。代わりに、Herb Sutterのアドバイスに従ってください(C ++の場合Dr Dobbsの記事When and How to Use Exceptions、および彼のAndrei Alexandrescu著の本、C ++ Coding Standardsで入手可能):例外をスローするのは、

  • 前提条件が満たされていない(通常、次のいずれかが不可能になる)または
  • 代替案は事後条件を満たさないか、または
  • 代替案は不変条件を維持できません。

なぜこれが良いのですか?前提条件、事後条件、および不変条件に関するいくつかの質問で質問を置き換えませんか?これはいくつかの関連する理由により優れています。

  • 前提条件、事後条件、および不変量は、プログラム(その内部API)の設計特性ですが、決定throwは実装の詳細です。設計とその実装を別々に検討する必要があることを私たちに強く強制します。メソッドを実装する際の私たちの仕事は、設計の制約を満たすものを生成することです。
  • これにより、前提条件、事後条件、および不変量の観点から考える必要が生じます。これらは、メソッドの呼び出し元が行うべき唯一の仮定であり、正確に表現され、プログラムのコンポーネント間の疎結合を可能にします。
  • その疎結合により、必要に応じて実装をリファクタリングできます。
  • 事後条件と不変条件はテスト可能です。事後条件は、ユニットテストコードがチェック(アサート)できる述語であるため、ユニットテストが容易なコードになります。
  • 事後条件の観点から考えると、例外を使用するための自然なスタイルである事後条件として成功するデザインが自然に生成さます。プログラムの通常の(「幸せな」)実行パスは直線的に配置され、すべてのエラー処理コードがcatch句に移動します。

10

例外をいつ使用するかについて、厳格で迅速なルールはないと思います。ただし、それらを使用する、または使用しないことには十分な理由があります。

例外を使用する理由:

  • 一般的なケースのコードフローはより明確です
  • 複雑なエラー情報をオブジェクトとして返すことができます(ただし、参照によって渡されるエラーの "out"パラメーターを使用してこれを実現することもできます)
  • 言語は通常、例外が発生した場合の整頓されたクリーンアップを管理するためのいくつかの機能を提供します(C#で使用してRAIIをC ++で使用して、最終的にJavaで試してください)。
  • 例外がスローされない場合、リターンコードをチェックするよりも実行が速くなることがあります。
  • Javaでは、チェックされた例外を宣言またはキャッチする必要があります(ただし、これは理由になる場合があります)。

例外を使用しない理由:

  • エラー処理が単純な場合は、やり過ぎになることがあります。
  • 例外が文書化または宣言されていない場合、例外は呼び出しコードによって捕捉されない可能性があります。これは、呼び出しコードが戻りコードを無視した場合よりも悪い場合があります(アプリケーションの終了とサイレントエラー-シナリオによってはさらに悪い場合があります)。
  • C ++では、例外を使用するコードは例外に対して安全でなければなりません(例外をスローしたりキャッチしたりせず、スローする関数を間接的に呼び出したとしても)。
  • C ++では、関数がいつスローするかを判断するのは難しいため、それらを使用する場合は例外の安全性に偏執的でなければなりません。
  • 例外のスローとキャッチは、通常、リターンフラグのチェックに比べて大幅にコストがかかります。

一般的に、C ++またはC#よりもJavaで例外を使用する傾向があります。なぜなら、宣言されているかどうかにかかわらず、例外は基本的には関数の正式なインターフェースの一部であると私は考えているためです。呼び出しコードを壊す。Java IMOでそれらを使用する最大の利点は、呼び出し元が例外を処理する必要があることを知っていることです。これにより、正しい動作の可能性が向上します。

このため、どの言語でも、コードのレイヤーまたはAPIのすべての例外を共通のクラスから派生させ、コードの呼び出しで常にすべての例外を確実にキャッチできるようにします。また、APIまたはライブラリを作成するときに、実装固有の例外クラスをスローすることは悪いと思います(つまり、呼び出し元が受け取る例外がインターフェイスのコンテキストで理解できるように、下位層からの例外をラップします)。

Javaは、一般的な例外とランタイム例外を宣言する必要がないという点で区別しています。エラーがプログラムのバグの結果であることがわかっている場合にのみ、ランタイム例外クラスを使用します。


5

例外クラスは「通常の」クラスに似ています。新しいクラスは、異なるタイプのオブジェクトであり、フィールドや操作が異なる場合に作成します。

経験則として、例外の数と例外の細分性のバランスをとる必要があります。メソッドが4〜5個以上の異なる例外をスローする場合、おそらくそれらの一部をより「一般的な」例外(たとえば、「AuthenticationFailedException」)にマージし、例外メッセージを使用して問題の詳細を説明できます。コードがそれぞれを異なる方法で処理しない限り、多くの例外クラスを作成する必要はありません。もしそうなら、発生したエラーを含む列挙型を返す必要がありますか?この方法では少しクリーンになります。


5

ループ内で実行されているコードで、例外が何度も発生する可能性がある場合、例外をスローすることは良いことではありません。これは、Nが大きい場合は非常に遅いためです。ただし、パフォーマンスが向上しない場合、カスタム例外をスローしても問題はありません。問題。それらがすべて継承するBaseException(BaseExceptionなど)があることを確認してください。BaseExceptionはSystem.Exceptionを継承しますが、すべての例外はBaseExceptionを継承します。例外タイプのツリーを作成して同様のタイプをグループ化することもできますが、これはやりすぎかもしれません。

したがって、簡単な答えは、それが重大なパフォーマンスのペナルティを引き起こさない場合(多くの例外をスローしない限りそれは起こらないはずです)、次に進みます。


ループ内の例外についてのコメントが本当に気に入り、自分で試してみようと思いました。ループint.MaxValue時間を実行し、「ゼロ除算」例外を生成するサンプルプログラムを作成しました。除算の前に被除数がゼロでないかどうかを確認していた IF / ELSEバージョンは6082ミリ秒と15407722ティックで完了しましたが、例外を生成してキャッチしていたTRY / CATCHバージョンは28174385で完了しました。 msおよび71371326155ティック:if / elseバージョンの4632倍の数。
user1451111

3

私はそこのジャポロックに同意します-あなたが手術の結果について確信が持てないとき、受け入れを投げます。APIへの呼び出し、ファイルシステムへのアクセス、データベース呼び出しなど。プログラミング言語の「境界」を超えて移動しているときはいつでも。

追加したいのですが、標準の例外を自由にスローしてください。「異なる」何かをするつもりでない限り(無視、電子メール、ログ、Twitterのクジラの画像を表示するなど)、カスタムの例外を気にしないでください。


3

例外をスローするための経験則は非常に簡単です。これは、コードがUNRECOVERABLE INVALID状態になったときに行います。データが危険にさらされている場合、またはそれまでに発生した処理を巻き戻すことができない場合は、データを終了する必要があります。実際、他に何ができますか?処理ロジックは最終的に他の場所で失敗します。何らかの方法で回復できる場合は、それを実行して例外をスローしません。

あなたの特定のケースでは、あなたがお金の引き出しを受け入れるような愚かなことを強制され、その後ユーザー/パスワードをチェックするだけである場合、何か悪いことが起こったことを通知し、それ以上の損傷を防ぐために例外をスローすることによってプロセスを終了する必要があります。


2

一般に、「例外的」である、アプリケーションで発生する可能性のあるすべてに対して例外をスローする必要がある

あなたの例では、これらの例外は両方とも、パスワード/ユーザー名検証を介して呼び出しているように見えます。その場合、誰かがユーザー名/パスワードを間違って入力することは本当に例外的ではないと主張することができます。

これらはUMLのメインフローに対する「例外」ですが、処理においてはより「分岐」します。

passwdファイルまたはデータベースにアクセスしようとしてアクセスできなかった場合、それは例外的なケースであり、例外をスローする必要があります。


passwdファイルまたはデータベースにアクセスしようとしてアクセスできなかった場合、それは例外的なケースであり、例外をスローする必要があります。. NETフレームワークなどのほとんどの言語フレームワークは、ファイルの存在を確認するためのAPIも提供しています。ファイルに直接アクセスする前に、それらを使用しないでください。
user1451111

2

まず、APIのユーザーが特定の細かい障害に関心がない場合、それらに特定の例外を設定しても意味がありません。

多くの場合、ユーザーにとって何が役立つかを知ることができないため、特定の例外を設定することをお勧めしますが、共通のクラス(例:std :: exceptionまたはC ++の派生物)から継承するようにします。これにより、クライアントは、選択した場合に特定の例外をキャッチしたり、気にしない場合により一般的な例外をキャッチしたりできます。


2

例外は、異常な動作、エラー、障害などのイベントを対象としています。機能的な動作、ユーザーエラーなどは、代わりにプログラムロジックで処理する必要があります。不正なアカウントまたはパスワードは、ログインルーチンのロジックフローの予想される部分であるため、例外なくこれらの状況を処理できるはずです。


2

例外の使用に関して哲学的問題があります。基本的に、特定のシナリオが発生することを期待していますが、明示的に処理するのではなく、「別の場所」で処理するように問題を先送りしています。そして、その「どこか」がどこにあるかは、誰でも推測できます。


2

一般的に、あらゆる原理主義は地獄につながると思います。

例外主導のフローで終了したくないのは確かですが、例外を完全に回避することも悪い考えです。両方のアプローチのバランスを見つける必要があります。私がしなかったことは、例外的な状況ごとに例外タイプを作成することです。それは生産的ではありません。

私が一般的に好むのは、システム全体で使用される2つの基本的なタイプの例外、LogicalExceptionTechnicalExceptionを作成することです。これらは、必要に応じてサブタイプによってさらに区別できますが、通常は必要ありません。

技術的な例外は、データベースサーバーのダウン、Webサービスへの接続がIOExceptionをスローしたなど、本当に予期しない例外を示します。

一方、論理的な例外は、それほど深刻ではないエラーの状況を上位層に伝達するために使用されます(通常、いくつかの検証結果)。

論理的な例外でさえ、プログラムフローを制御するために定期的に使用することを意図したものではなく、フローが実際に終了する必要がある状況を強調するためのものであることに注意してください。Javaで使用される場合、両方の例外タイプはRuntimeExceptionサブクラスであり、エラー処理は非常にアスペクト指向です。

したがって、ログインの例では、AuthenticationExceptionのようなものを作成し、UsernameNotExistingPasswordMismatchなどの列挙値によって具体的な状況を区別するのが賢明かもしれません。そうすれば、巨大な例外階層を持つことにならず、catchブロックを保守可能なレベルに保つことができます。 。例外を分類し、ユーザーに何をどのように伝播するかを十分に理解しているため、いくつかの一般的な例外処理メカニズムを簡単に使用することもできます。

私たちの典型的な使用法は、ユーザーの入力が無効な場合に、Webサービスの呼び出し中にLogicalExceptionをスローすることです。例外はSOAPFaultの詳細にマーシャリングされ、クライアントで再度非マーシャリングされます。その結果、特定のWebページ入力フィールドに検証エラーが表示されます。これは、例外がそのフィールドに適切にマッピングされているためです。

例外はこれだけではありません。例外をスローするためにWebサービスにアクセスする必要はありません。例外的な状況(フェイルファストが必要な場合など)では、これを自由に行うことができます-それはすべてあなたの裁量です。


2

私にとって、必要な技術またはビジネスルールが失敗した場合、例外がスローされます。たとえば、車のエンティティが4つのタイヤの配列に関連付けられている場合... 1つ以上のタイヤがnullの場合...例外は "NotEnoughTiresException"である必要があります。これは、システムのさまざまなレベルでキャッチされ、重要なロギングによる意味。さらに、ヌルをフロー制御して車のインスタンス化を防止しようとするだけの場合。そもそも問題の原因を見つけることは決してないかもしれませんが、そもそもタイヤがnullであるはずがないからです。


1

例外のスローを回避する主な理由は、例外のスローに伴うオーバーヘッドが多いためです。

以下の記事で述べていることの1つは、例外は例外的な条件とエラーのためのものであるということです。

間違ったユーザー名は必ずしもプログラムエラーではなく、ユーザーエラーです...

.NET内の例外の適切な開始点は次のとおりです。http//msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx


1

例外をスローするとスタックが解放され、パフォーマンスに影響を与えます(許可された最新の管理環境では改善されています)。それでも、ネストされた状況で例外を繰り返しスローしてキャッチすることは悪い考えです。

おそらくそれよりも重要な例外は、例外は例外的な状態を意味します。通常の制御フローには使用しないでください。コードの可読性が低下します。


1

私は3つのタイプの状態をキャッチします。

  1. 入力不良または欠落は例外ではありません。クライアント側のjsとサーバー側の正規表現の両方を使用して、属性を検出、設定し、メッセージのある同じページに転送します。

  2. AppException。これは通常、コードで検出してスローする例外です。言い換えれば、これらはあなたが期待するものです(ファイルは存在しません)。ログに記録し、メッセージを設定して、一般的なエラーページに戻ります。このページには通常、何が起こったかについての情報が少しあります。

  3. 予期しない例外。これらはあなたが知らないものです。詳細とともにログに記録し、一般的なエラーページに転送します。

お役に立てれば


1

セキュリティはあなたの例と一致しています:ユーザー名が存在することを攻撃者に知らせてはいけませんが、パスワードは間違っています。これは、共有する必要のない追加情報です。「ユーザー名またはパスワードが間違っています」と言ってください。


1

単純な答えは、操作が不可能なときはいつでもです(アプリケーションまたはビジネスロジックに違反するため)。メソッドが呼び出され、そのメソッドが実行するように記述されていることを実行できない場合は、例外をスローします。良い例は、提供されたパラメーターを使用してインスタンスを作成できない場合、コンストラクターが常にArgumentExceptionsをスローすることです。別の例は、InvalidOperationExceptionです。これは、クラスの別のメンバーの状態が原因で操作を実行できない場合にスローされます。

あなたのケースでは、Login(username、password)のようなメソッドが呼び出された場合、ユーザー名が有効でない場合、UserNameNotValidExceptionをスローすることは確かに正しく、パスワードが正しくない場合はPasswordNotCorrectExceptionをスローします。ユーザーは提供されたパラメーターを使用してログインできない(つまり、認証に違反するため不可能です)ため、例外をスローします。2つの例外がArgumentExceptionから継承される場合があります。

そうは言っても、ログイン失敗が非常に一般的である可能性があるために例外をスローしたくない場合は、代わりに、さまざまな失敗を表す型を返すメソッドを作成する方法があります。次に例を示します。

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

ほとんどの開発者は、例外をスローすることによって生じるオーバーヘッドのため、例外を回避するように教えられています。リソースを意識することは素晴らしいことですが、通常はアプリケーション設計を犠牲にすることはありません。これがおそらく、2つの例外をスローしないように言われた理由です。例外を使用するかどうかは、通常、例外が発生する頻度によって決まります。それがかなり一般的またはかなり期待できる結果である場合、これはほとんどの開発者が例外を回避し、代わりにリソースの想定消費のために失敗を示す別のメソッドを作成するときです。

Try()パターンを使用して、今説明したようなシナリオで例外の使用を回避する例を次に示します。

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

1

私の考えでは、基本的な問題は、条件が発生した場合に呼び出し元が通常のプログラムフローを継続することを期待するかどうかです。わからない場合は、doSomethingメソッドとtrySomethingメソッドを別々にして(前者はエラーを返し、後者は返さない)、失敗した場合に例外をスローするかどうかを示すパラメーターを受け入れるルーチンを使用します)。リモートシステムにコマンドを送信し、応答を報告するクラスを検討してください。特定のコマンド(再起動など)により、リモートシステムは応答を送信しますが、一定時間応答しなくなります。したがって、「ping」コマンドを送信して、リモートシステムが適切な時間内に応答するかどうかを、例外がスローされなくても例外をスローする必要なしに見つけることができると便利です。t(呼び出し元は、最初の数回の「ping」試行が失敗することをおそらく期待しますが、1つは最終的には機能します)。一方、次のような一連のコマンドがあるとします。

  exchange_command( "open tempfile");
  exchange_command( "書き込み一時ファイルデータ{何でも}");
  exchange_command( "書き込み一時ファイルデータ{何でも}");
  exchange_command( "書き込み一時ファイルデータ{何でも}");
  exchange_command( "書き込み一時ファイルデータ{何でも}");
  exchange_command( "一時ファイルを閉じる");
  exchange_command( "tempfile to realfile");

シーケンス全体を中止するための操作の失敗が必要になります。各操作をチェックして成功したことを確認することもできますが、コマンドが失敗した場合にexchange_command()ルーチンに例外をスローさせる方が便利です。

実際、上記のシナリオでは、いくつかの障害処理モードを選択するパラメーターを用意すると便利です。例外をスローしない、通信エラーのみの例外をスローする、コマンドが "成功を返さない場合は例外をスローする"表示。


1

「PasswordNotCorrectException」は、例外を使用する良い例ではありません。ユーザーが自分のパスワードを間違って取得することは予期されていることなので、それはほとんど例外ではありません。あなたはおそらくそれから回復し、素晴らしいエラーメッセージを表示するので、それは単なる妥当性チェックです。

未処理の例外は最終的に実行を停止します-これは良いことです。false、null、またはエラーコードを返す場合は、プログラムの状態をすべて自分で処理する必要があります。どこかで状態を確認するのを忘れると、プログラムが間違ったデータで実行を続け、どこでが起こったかを理解するのに苦労するかもしれません。

もちろん、空のcatchステートメントでも同じ問題が発生する可能性がありますが、少なくともそれらを見つけるのは簡単であり、ロジックを理解する必要はありません。

したがって、経験則として:

不要な場所、またはエラーから回復できない場所で使用してください。


0

その条件には、少し一般的な例外を使用できます。たとえば、ArgumentExceptionは、メソッドへのパラメータに問題が発生したときに使用されることを意図しています(ArgumentNullExceptionを除く)。通常、LessThanZeroException、NotPrimeNumberExceptionなどの例外は必要ありません。メソッドのユーザーについて考えてください。彼女が具体的に処理する必要がある条件の数は、メソッドがスローする必要がある例外のタイプの数と同じです。このようにして、どの程度詳細な例外が発生するかを判断できます。

ちなみに、ライブラリのユーザーが例外を回避できるように、常にいくつかの方法を提供するようにしてください。TryParseは良い例であり、int.Parseを使用して例外をキャッチする必要がないように存在します。あなたの場合、ユーザー(またはあなた)が多くの例外処理を行う必要がないように、ユーザー名が有効であるか、パスワードが正しいかどうかを確認するいくつかの方法を提供することができます。これにより、コードが読みやすくなり、パフォーマンスが向上することが期待されます。


0

最終的には、このようなアプリケーションレベルのエラーを例外処理を使用して処理する方がよいか、ステータスコードを返すなどの独自のホームロールメカニズムを介して処理する方がよいかによって決定が決まります。私はどちらが良いかについて厳格な規則はないと思いますが、私は検討します:

  • 誰があなたのコードを呼んでいますか?これはある種のパブリックAPIですか、それとも内部ライブラリですか?
  • どの言語を使用していますか?たとえばJavaの場合、(チェックされた)例外をスローすると、無視できる戻りステータスとは対照的に、呼び出し側に何らかの方法でこのエラー条件を処理する明示的な負担がかかります。それは良いか悪いかである。
  • 同じアプリケーションの他のエラー状態はどのように処理されますか?呼び出し元は、システム内の他のものとは異なり、特異な方法でエラーを処理するモジュールを扱いたくないでしょう。
  • 問題のルーチンでいくつの問題が発生する可能性があり、それらはどのように異なる方法で処理されますか?さまざまなエラーを処理する一連のキャッチブロックとエラーコードの切り替えの違いを検討してください。
  • 返す必要のあるエラーに関する構造化された情報はありますか?例外をスローすることで、ステータスを返すだけでなく、この情報を配置することができます。

0

例外には主に2つのクラスがあります。

1)システム例外(データベース接続の切断など)または2)ユーザー例外。(例:ユーザー入力の検証、「パスワードが正しくありません」)

独自のユーザー例外クラスを作成すると便利です。ユーザーエラーをスローしたいときは、別の方法で処理したい(つまり、ユーザーにリソースエラーを表示したい)場合は、メインエラーハンドラーでオブジェクトタイプを確認するだけです。 :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

0

例外が適切かどうかを判断するときに考慮すべきいくつかの便利な点:

  1. 例外候補が発生した後に実行するコードのレベル-つまり、コールスタックの何層を巻き戻すか 通常、例外は発生した場所のできるだけ近くで処理します。ユーザー名/パスワードの検証では、通常、例外を発生させるのではなく、同じコードブロックで失敗を処理します。したがって、例外はおそらく適切ではありません。(OTOH、ログイン試行が3回失敗した後、制御フローが別の場所に移動する可能性があり、ここでは例外が適切な場合があります。)

  2. このイベントは、エラーログで確認したいものですか?すべての例外がエラーログに書き込まれるわけではありませんが、エラーログのこのエントリが役立つかどうか、つまり、何かについて何かしようとするか、それとも無視するかを尋ねることは役立ちます。

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