例外が入力検証にとってそれほど悪いと言われているのはなぜですか?

StackOverflow https://stackoverflow.com/questions/410558

質問

<!> quot;例外は例外的なケースである<!> quot; [a]、ただし、繰り返されるだけでなく、 over およびもう一度、私はこの事実の実際の理由を見つけたことがありません。

それらが実行を停止するということは、単純な条件付きロジックには使用したくないのですが、なぜ入力検証をしないのですか?

入力のグループをループし、各例外をキャッチしてユーザー通知のためにグループ化することにしたとしましょう...これはどういうわけか<!> quot; wrong <!> quot;ユーザーは常に間違った入力を入力しますが、そのポイントはセマンティクスに基づいているようです

入力は予期されたものではないため、例外的です。例外をスローすると、StringValueTooLong、IntegerValueTooLow、InvalidDateValueなど、何が間違っていたかを正確に定義できます。なぜこれが間違っていると考えられますか

例外をスローする別の方法は、エラーコードを返す(最終的に収集する)か、エラー文字列をさらに悪化させることです。次に、これらのエラー文字列を直接表示するか、エラーコードを解析してから、対応するエラーメッセージをユーザーに表示します。例外は順応性のあるエラーコードと見なされませんか?言語に既に組み込まれている例外機能で一般化できるのに、エラーコードとメッセージの別のテーブルを作成するのはなぜですか?

また、私はマーティン・ファウラーによるこの記事を見つけました -通知パターン。実行を停止しない例外以外の何かとしてこれをどのように見ているのかわかりません。

a:どこでも例外について読んだことがあります。

---編集---

多くの素晴らしい点が指摘されています。ほとんどの点についてコメントし、良い点を+付けましたが、まだ完全には納得していません。

入力検証を解決するための適切な手段として例外を提唱するつもりはありませんが、ほとんどの代替解決策が変装した単なる例外であるように思えるのに、この実践が非常に悪いと考えられる理由を見つけたいと思います。

役に立ちましたか?

解決

これらの回答を読んで、<!> quot;例外は例外的な条件にのみ使用すべき<!> quot;と言うのは非常に役に立ちません。これは、<!> quot;例外条件<!> quot;とは何かという疑問を投げかけます。これは主観的な用語であり、最良の定義は<!> quot;通常のロジックフローが処理しない条件<!> quot;です。つまり、例外条件とは、例外を使用して対処する条件のことです。

それを定義としては大丈夫です。とにかくそれよりも近くなるとは思いません。しかし、あなたはそれがあなたが使用している定義であることを知っている必要があります。

特定のケースで例外に反対する場合、条件の宇宙を<!> quot; exceptional <!> quot;に分割する方法を説明する必要があります。および<!> quot; nonexceptional <!> quot;。

いくつかの点で、質問に答えるのと似ています。<!> quot;手続き間の境界はどこですか?<!> quot;答えは、<!> quot;どこにbeginとend <!> quot;を入れても、経験則とそれらを置く場所を決定するためのさまざまなスタイルについて話すことができます。厳格なルールはありません。

他のヒント

「不正な」入力を入力したユーザーも例外ではありません。当然のことです。

通常の制御フローには例外を使用しないでください。

過去には、多くの著者が例外は本質的に高価だと言ってきました。 Jon Skeetはこれに反してブログを書いており(そしてSOについての回答でいくつかの時間を言及しました)、彼らは報告されているほど高価ではないと述べています(私は<!>#8217;それらをタイトなループで使用することを推奨しません! / p>

それらを使用する最大の理由は、<!>#8216;意図の記述<!>#8217;つまり、例外処理ブロックが表示された場合、通常のフロー以外で処理される例外的なケースがすぐに表示されます。

既に述べたものとは別の重要理由が1つあります:

例外を例外のみで使用する場合は、デバッガー設定<!> quot;例外がスローされたら停止する<!> quot;でデバッガーで実行できます。これは、問題を引き起こしている正確な行でデバッガーにドロップするため、非常に便利です。この機能を使用すると、毎日かなりの時間を節約できます。

C#では、特にTryParseメソッドをすべての数値クラスに追加した後、これが可能です(そして私は心からお勧めします)。一般に、標準ライブラリは<!> quot; bad <!> quot;を必要とせず、使用しません。例外処理。この標準に準拠していないC#コードベースにアプローチする場合、stop-om-throwは非常に重要であるため、常に例外なしの通常のケースに変換することになります。

firebug javascriptデバッガーでは、ライブラリが例外を不適切に使用しない限り、これも実行できます。

Javaをプログラムするとき、これは実際には不可能です。なぜなら、多くの標準的なJavaライブラリを含む、例外的でない場合には例外を使用しているからです。そのため、この時間節約機能は、実際にはJavaで使用できません。これはチェックされた例外によるものだと思いますが、それらがどのように悪かを暴言することはありません。

エラーと例外<!>#8211;何、いつ、どこで?

例外はエラーを報告することを目的としているため、コードがより堅牢になります。例外をいつ使用するかを理解するには、まずエラーとは何か、エラーではないものを理解する必要があります。

機能は作業の単位であり、障害はエラーまたは機能への影響に基づいて判断する必要があります。関数 f 内の失敗は、 f が呼び出し先<!>#8217; s 前提条件 f <!>#8217;自身の事後条件のいずれかを達成する、または f である不変を再確立する>メンテナンスの責任を共有します。

エラーには3種類あります:

  • 呼び出す必要がある別の関数の前提条件(パラメーターの制限など)を関数が満たすことを妨げる条件。
  • 関数が独自の事後条件の1つを確立できないようにする条件(たとえば、有効な戻り値の生成は事後条件です)。そして
  • 関数が維持に関与する不変式を再確立するのを妨げる条件。これは特別な種類の事後条件であり、特にメンバー関数に適用されます。すべての非プライベートメンバー関数の重要な前提条件は、クラス<!>#8217;の不変式を再確立する必要があることです。

その他の条件はエラーではない ため、エラーとして報告しないでください。

例外が入力検証に非常に悪いと言われるのはなぜですか?

<!>#8220; input <!>#8221;が多少曖昧に理解されているためだと思います。 関数の入力またはフィールドの値のいずれかを意味します。後者は失敗した関数の一部でない限り例外をスローします<!>#8217; t。

違いは特定のクラスのコントラクト、つまり

に依存すると思います

ユーザー入力を処理することを目的とするコード、およびそれを防御的にプログラムする(つまり、サニタイズする)場合、無効な入力に対して例外をスローするのは間違っています-予想されます。

ユーザーによって発生した可能性のある、すでにサニタイズおよび検証された入力を処理するコードの場合、禁止する必要がある入力が見つかった場合、例外のスローは有効です。その場合、呼び出し元のコードは契約に違反しており、サニタイズまたは呼び出し元のコードのバグを示しています。

  1. メンテナンス性-例外の作成 GOTOとは異なり、奇数のコードパス。
  2. 使いやすさ(他のクラスの場合)- 他のクラスはそれを信頼できます ユーザーから発生した例外 入力クラスは実際のエラーです
  3. パフォーマンス-ほとんどの言語では、 例外はパフォーマンスを引き起こし、 メモリ使用量のペナルティ。
  4. セマンティクス-言葉の意味 関係ない悪い入力は <!> quot; exceptional <!> quot;。

例外を使用する場合、エラー処理コードはエラーの原因となるコードから分離されます。これは例外処理の目的です。例外条件であるため、エラーはローカルで処理できないため、例外はより高い(および未知の)スコープにスローされます。処理されない場合、アプリケーションはハードが完了する前に終了します。

ユーザー入力の検証などの単純なロジック操作を行っているときに例外をスローする場合は、非常に非常に非常に間違っていることになります。

  

入力は予期されたものではなく、   したがって、例外的です。

この声明は、私とまったく相性が悪い。 UIがユーザー入力を制限し(たとえば、最小値/最大値を制限するスライダーの使用)、特定の条件をアサートできるようになりました-エラー処理は不要です。または、ユーザーはごみを入力できますが、これが起こると予想され、処理する必要があります。どちらか-ここに行く例外は何もありません。

  

例外をスローすると、   何が間違っていたかを正確に定義する   StringValueTooLongまたはまたは   IntegerValueTooLowまたはInvalidDateValue   または何でも。なぜこれが考慮されるのか   間違っていますか?

私はこれを超えて、悪に近いと考えています。抽象ErrorProviderインターフェイスを定義するか、単純なコードではなく、エラーを表す複雑なオブジェクトを返すことができます。エラーレポートの取得方法には、非常に多くのオプションがあります。 便利であるため、例外を使用するとそのため間違っている。この段落を書いているだけで汚い気がします。

希望として例外をスローすることを考えてください。最後のチャンス。祈り。ユーザーの入力を検証しても、これらの条件が発生することはありません。

意見の不一致の一部は、「ユーザー入力」が何を意味するかについてのコンセンサスの欠如が原因である可能性はありますか?そして実際、どのレイヤーでコーディングしているのか。

GUIユーザーインターフェイスまたはWebフォームハンドラーをコーディングしている場合、無効な入力は人間の入力指から直接入力されるため、予期しない場合があります。

MVCアプリのモデル部分をコーディングしている場合、コントローラーが入力をサニタイズするように設計した可能性があります。モデルまでの無効な入力は実際には例外であり、そのように扱われる可能性があります。

プロトコルレベルでサーバーをコーディングしている場合、クライアントがユーザー入力をチェックしていることを合理的に期待できます。繰り返しますが、ここでの無効な入力は確かに例外です。これは、クライアントを100%信頼することとはまったく異なります(実際、非常に愚かなことです)-しかし、直接的なユーザー入力とは異なり、ほとんどの時間入力は問題ないと予測します。ここで線が多少ぼやけます。何かが発生する可能性が高いほど、例外を使用してそれを処理する必要が少なくなります。

これは、問題に関する言語的な視点です。

例外が入力検証にとってそれほど悪いと言われるのはなぜですか?

結論:

  • 例外は十分に明確に定義されていないため、異なる意見があります。
  • 間違った入力は、例外ではなく通常のものと見なされます。

思考?

おそらく、作成されるコードについての期待に帰着します。

  • クライアントは信頼できません
    • 検証は、サーバー側で行われます より強い: すべての検証はサーバー側で行われます。
    • 検証はサーバー側で行われるため、そこで行われることは期待されるであり、期待されるので、期待されることは例外ではありません。

ただし、

  • クライアントの入力は信頼できません
  • クライアントの入力検証が信頼できる
    • 検証が信頼できる場合、有効な入力を生成するために期待されることができます
    • 現在、すべての入力が有効であることが期待されています
    • 無効な入力は予期せず、例外です

例外は、コードを終了する良い方法です。

考慮すべきことは、コードが適切な状態にあるかどうかです。 私のコードが不適切な状態のままになるのはどうしてかわかりません。 接続は自動的に閉じられ、残りの変数はガベージコレクトされますが、問題は何ですか?

例外ではないものに対する例外処理に対する別の投票!

  1. .NETでは、JITコンパイラーは、例外がスローされない場合でも、特定のケースで最適化を実行しません。次の記事で説明しています。 http:// msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx http://msmvps.com/blogs/peterritchie/archive/2007/07/12/performance-implications-of-try-catch-finally-part-two.aspx

  2. 例外がスローされると、実際に<!> quot; expecting <!> quot;だった場合には不要なスタックトレースの情報が大量に生成されます。文字列をintなどに変換する場合によくある例外です。

一般に、ライブラリは例外をスローし、クライアントはそれらをキャッチし、それらに対してインテリジェントな処理を行います。ユーザー入力については、例外をスローする代わりに検証関数を作成するだけです。そのようなものについては例外が過剰に思えます。

例外にはパフォーマンスの問題がありますが、GUIコードでは一般にそれらを心配する必要はありません。では、検証の実行にさらに100ミリ秒かかるとしたらどうでしょうか?ユーザーはそのことに気付かないでしょう。

ある意味では難しい呼び出しです-一方では、ユーザーが郵便番号のテキストボックスに余分な数字を入力し、例外を処理するのを忘れたため、アプリケーション全体がクラッシュすることを望まないかもしれません。一方、「早期フェイル、フェイルハード」アプローチにより、バグを迅速に発見して修正し、貴重なデータベースを正常に保つことができます。一般的に、ほとんどのフレームワークはUIエラーチェックに例外処理を使用しないことを推奨し、。

例外は入力検証に使用すべきではありません。例外は例外的な状況で使用されるべきであるだけでなく(誤ったユーザーエントリが指摘されていないため)、例外的なコードを作成するからです(素晴らしい意味ではありません)。

ほとんどの言語の例外の問題は、プログラムフローのルールを変更することです。これは、有効なフローがどうあるべきかを把握できないため、例外をスローして取得するだけの真に例外的な状況では問題ありません。ただし、フローがどうあるべきかを知っている場合は、そのフローを作成する必要があります(リストされている場合、ユーザーに何らかの情報を再入力する必要があることを伝えるメッセージを出すことです)。

私が日々取り組んでいるアプリケーションでは例外が本当に使いすぎており、ログイン時にユーザーが間違ったパスワードを入力した場合でも、ロジックはアプリケーションが望んでいないものであるため、例外の結果になります。しかし、プロセスが正しいか間違っているかの2つの結果のいずれかを持っている場合、間違っていても、どれだけ間違っていても例外的であるとは言えません。

このコードでの作業で見つかった主な問題の1つは、デバッガーに深く関与することなくコードのロジックに従うことです。デバッガーは優れていますが、ユーザーが間違ったパスワードを入力した場合に発生するロジックを追加する必要があります。

単なる例外ではなく、本当に例外的な実行の例外を保持します。パスワードを間違えることを強調している場合は例外ではありませんが、ドメインサーバーに接続できないことは可能性があります!

検証エラーに対して例外がスローされるのを見ると、例外をスローするメソッドが一度に多くの検証を実行していることがよくわかります。例:

public bool isValidDate(string date)
{
    bool retVal = true;
    //check for 4 digit year
    throw new FourDigitYearRequiredException();
    retVal = false;

    //check for leap years
    throw new NoFeb29InANonLeapYearException();
    retVal = false;
    return retVal;
}

このコードは非常に壊れやすく、数か月から数年にわたってルールが積み重なっていくため、保守が難しい傾向があります。私は通常、検証をブール値を返す小さなメソッドに分割することを好みます。ルールの調整が簡単になります。

public bool isValidDate(string date)
{
    bool retVal = false;
    retVal = doesDateContainAFourDigitYear(date);
    retVal = isDateInALeapYear(date);
    return retVal;
}

public bool isDateInALeapYear(string date){}

public bool doesDateContainAFourDigitYear(string date){}

既に述べたように、エラーに関する情報を含むエラー構造体/オブジェクトを返すことは素晴らしいアイデアです。最も明らかな利点は、検証でWhack-A-Moleを再生させる代わりに、それらを収集してユーザーにすべてのエラーメッセージを一度に表示できることです。

iは、両方のソリューションの組み合わせを使用しました。 検証関数ごとに、検証ステータス(エラーコード)を埋めるレコードを渡します。 関数の最後に、検証エラーが存在する場合、例外をスローします。この方法では、各フィールドに例外をスローせず、一度だけスローします。また、データが無効な場合に実行を継続したくないため、例外をスローすると実行が停止することを利用しました。

たとえば

procedure Validate(var R:TValidationRecord);
begin
  if Field1 is not valid then
  begin
    R.Field1ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end; 
  if Field2 is not valid then
  begin
    R.Field2ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;
  if Field3 is not valid then
  begin
    R.Field3ErrorCode=SomeErrorCode;
    ErrorFlag := True; 
  end;

  if ErrorFlag then
    ThrowException
end;

ブール値のみに依存している場合、私の関数を使用する開発者はこれを記述に考慮する必要があります:

if not Validate() then
  DoNotContinue();

しかし、彼は忘れてValidate()を呼び出すだけかもしれません(そうすべきではないことは知っていますが、そうするかもしれません)。

そのため、上記のコードでは、2つの利点が得られました。 検証関数の1つだけの例外。 2例外は、キャッチされなくても実行を停止し、テスト時に表示されます。

8年後、CQSパターンを適用しようとして同じジレンマに直面しています。入力検証で例外がスローされる可能性がありますが、制約が追加されています。入力が失敗した場合、ValidationException、BrokenRuleExceptionなどの1種類の例外をスローする必要があります。すべてを処理することは不可能になるため、異なるタイプの束をスローしないでください。これにより、すべての壊れたルールのリストを1か所で取得できます。検証(SRP)を行う単一のクラスを作成し、少なくとも1つのルールが破られた場合に例外をスローします。そうすれば、あなたは1つのキャッチで1つの状況を処理し、あなたが良いことを知っています。どのコードが呼び出されても、そのシナリオを処理できます。これにより、有効な状態にあるか、そこに到達していないことがわかっているため、すべてのコードがずっときれいになります。

私にとって、ユーザーから無効なデータを取得することは、通常予想されることではありません。 (すべてのユーザーが最初に無効なデータを送信する場合は、UIを再確認します。)ユーザーであるか他のソースであるかにかかわらず、真の意図を処理できないデータは処理を中止する必要があります。それがユーザー入力である場合と、これが必須であると言うクラスのフィールドである場合、単一のデータからArgumentNullExceptionをスローすることとはどう違いますか。

確かに、最初に検証を行い、すべての単一の<!> quot; command <!> quot;に同じ定型コードを書くことができますが、無効なユーザー入力をすべて1か所でキャッチするよりもメンテナンスの悪夢だと思います関係なく同じ方法で処理されるトップ。 (コードが少ない!)パフォーマンスヒットは、ユーザーが無効なデータを指定した場合にのみ発生しますが、これはそれほど頻繁には発生しません(または、UIが悪い)。とにかく、クライアント側のすべてのルールをサーバー上で書き換える必要があるため、一度書くだけで、AJAX呼び出しを行い、<!> lt; 500ミリ秒の遅延により、コーディング時間を大幅に節約できます(すべての検証ロジックを配置する場所は1つだけです)。

また、ASP.NETを使用して簡単に検証を行うことができますが、他のUIで検証ロジックを再利用したい場合は、ASP.NETにベイクされるのでできません。使用しているUIに関係なく、下に何かを作成し、上でそれを処理する方が良いでしょう。 (少なくとも私の2セント。)

<!> quot;例外は通常の制御フロー<!> quot;には使用すべきではないというMitchに同意します。コンピューターサイエンスのクラスで覚えていることから、例外をキャッチするのは高価です。ベンチマークを実行しようとしたことはありませんが、if / elseとtry / catchのパフォーマンスを比較することは興味深いでしょう。

例外の使用に関する1つの問題は、一度に1つの問題のみを検出する傾向です。ユーザーはそれを修正して再送信しますが、別の問題を見つけるためだけです!解決する必要がある問題のリストを返すインターフェースは、はるかに使いやすいです(ただし、例外にラップされる可能性があります)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top