25

このコードについて同僚と議論しています。

var y = null;
if (x.parent != null)
    y = x.parent.somefield;

私の見解は、コードがある場所では、x.parentNULL であってはならないということです。null の場合は重大な問題があるので、それを知りたいです。したがって、null チェックが存在してはならず、ダウンストリーム例外が発生します。

私の同僚は、これは防御的プログラミングだと言います。また、null チェックにより、コードがアプリケーションを壊さないようにします。

私の質問は、これは防御的なプログラミングですか? それとも悪い習慣ですか?

注: ポイントは誰が正しいかではありません。私はこの例から学ぼうとしています。

4

5 に答える 5

13

あなたの同僚は「防御的プログラミング」や例外を誤解しているようです。

防御的プログラミング

防御的プログラミングとは、特定の種類のエラーから保護することです。

この場合x.parent == null、メソッドは を使用する必要があるため、エラーになりますx.parent.SomeFieldparentnull の場合、 の値はSomeField明らかに無効になります。無効な値を使用して計算またはタスクを実行すると、誤った予測不可能な結果が生じる可能性があります。

したがって、この可能性から保護する必要があります。NullPointerException保護するための非常に良い方法は、 を発見した場合に を投げることx.parent == nullです。この例外により、 から無効な値を取得できなくなりますSomeField。無効な値を使用して計算を行ったり、タスクを実行したりすることはできません。そして、エラーが適切に解決されるまで、現在のすべての作業を中止します。

例外はエラーではないことに注意してください。無効な値parent実際のエラーです。例外は、実際には保護メカニズムです。例外は防御的なプログラミング手法であり、避けるべきものではありません。

C# は既に例外をスローしているため、実際には何もする必要はありません。実際、「防御的プログラミングの名の下に」あなたの同僚の努力は、言語によって提供される組み込みの防御的プログラミングを実際に元に戻しています。

例外

多くのプログラマーが例外について過度に偏執的であることに気付きました。例外自体はエラーではなく、単にエラーを報告するだけです。

あなたの同僚は、「null チェックは、コードがアプリケーションを壊さないことを確認します」と言います。これは、例外がアプリケーションを壊すと彼が信じていることを示唆しています。通常、アプリケーション全体を「壊す」ことはありません。

例外処理が不十分なためにアプリケーションが一貫性のない状態になると、例外によってアプリケーションが破損する可能性があります。(ただし、エラーが隠されている場合、これはさらに可能性が高くなります。) また、例外がスレッドを「エスケープ」すると、アプリケーションが壊れる可能性があります。(メイン スレッドをエスケープするということは、明らかに、プログラムがかなり不当に終了したことを意味します。しかし、子スレッドをエスケープすることでさえ十分に悪いため、オペレーティング システムにとって最適なオプションはアプリケーションを GPF することです。)

ただし、例外は現在の操作を中断 (中止) します。そして、これは彼らがしなければならないことです。と呼ばれるメソッドをコーディングすると、DoSomethingwhich が呼び出されるためですDoStep1。のエラーは、その仕事を適切に行うことができないことをDoStep1意味します。に電話しても意味がありません。DoSomethingDoStep2

ただし、ある時点で特定のエラーを完全に解決できる場合は、必ずそうしてください。ただし、「完全に解決する」ことに重点が置かれていることに注意してください。これは単にエラーを隠すという意味ではありません。また、エラーをログに記録するだけでは、通常、エラーを解決するには不十分です。これは、別のメソッドがメソッドを呼び出して正しく使用した場合、「解決されたエラー」が呼び出し元のジョブを適切に実行する能力に悪影響を及ぼさないという点に到達することを意味します。(発信者が何であっても。)

おそらく、エラーを完全に解決する最も良い例は、アプリケーションのメイン処理ループにあります。その仕事は、キュー内のメッセージを待ち、次のメッセージをキューから取り出し、適切なコードを呼び出してメッセージを処理することです。例外が発生し、メイン メッセージ ループに戻る前に解決されなかった場合は、解決する必要があります。そうしないと、例外がメイン スレッドをエスケープし、アプリケーションが終了します。

多くの言語は、標準フレームワークでデフォルトの例外ハンドラー (プログラマーがオーバーライド/インターセプトするメカニズムを備えたもの) を提供します。デフォルトのハンドラーは通常、ユーザーにエラー メッセージを表示し、例外を飲み込みます。

なんで?貧弱な例外処理を実装していなければ、プログラムは一貫した状態になります。現在のメッセージは中止され、次のメッセージは何も問題がなかったかのように処理できます。もちろん、このハンドラーを次のようにオーバーライドできます。

  • ログファイルに書き込みます。
  • トラブルシューティングのためにコール スタック情報を送信します。
  • 特定のクラスの例外を無視します。(たとえばAbort、おそらく以前にメッセージを表示したため、ユーザーに伝える必要さえないことを暗示している可能性があります。)

例外処理

最初に例外を発生させずにエラーを解決できる場合は、そうする方がクリーンです。ただし、エラーが最初に表示された時点で解決できない場合や、事前に検出できない場合があります。このような場合、エラーを報告するために例外を発生またはスローする必要があり、例外ハンドラー ( C# のcatchブロック) を実装して解決します。

注: 例外ハンドラー サーバーには 2 つの異なる目的があります。まず、エラー/例外発生したため、特にクリーンアップ (またはロールバック コード)を実行する場所を提供します。次に、エラーを解決して例外を飲み込む場所を提供します。注意: 前者のケースでは、例外が解決されていないため、例外が再発生/スローされることが非常に重要です。

例外をスローして処理することについてのコメントで、あなたは次のように述べています

これは別の誤解です。前の補足事項によると、次の場合にのみ例外処理が必要です。

  • エラーを解決できます。その場合は完了です。
  • または、ロールバック コードを実装する必要がある場所。

この懸念は、原因と結果の分析の欠陥によるものかもしれません。例外をスローしているという理由だけで、ロールバック コードは必要ありません。例外がスローされる理由は他にもたくさんあります。エラーが発生した場合、メソッドはクリーンアップを実行する必要があるため、ロールバック コードが必要です。つまり、いずれにしても例外処理コードが必要になります。これは、過剰な例外処理に対する最善の防御策は、エラーのクリーンアップの必要性を減らすように設計することであることを示唆しています。

したがって、過剰な例外処理を避けるために、「例外をスローしない」ことは避けてください。過剰な例外処理は良くないことに同意します (上記の設計上の考慮事項を参照してください)。しかし、エラーがあったことさえ知らなかったために、ロールバックすべきときにロールバックしないのは、はるかに悪いことです。

于 2014-06-16T13:43:29.557 に答える
2

私はそれを防御的プログラミングとはまったく呼びません-「ラララ、あなたの声が聞こえない」プログラミングと呼びます:) コードは潜在的なエラー状態を効果的に無視しているように見えるからです。

明らかに、あなたのコードで次に来るものについてはまったくわかりませんが、else句が含まれていないため、実際にx.parent null.

「null になる可能性はない」と「null にならないことが確実に確実に保証される」は、必ずしも同じではないことに注意してください。その場合、逆参照しようとすると、さらに下の行で例外が発生する可能性がありますy

y問題は、解決しようとしている問題 (「ドメイン」) のコンテキスト内でより受け入れられるものは何かということです。その種類は、後で何をしようとしているかによって異なります。

  • このコードの後にy​​いることnullが問題にならない場合 (後で の防御チェックを行うとしましょうy!=null)、それで問題ありません。個人的にはそのスタイルは好きではありませんが、すべての逆参照を防御的にチェックすることになります。あなたがクラッシュから離れたnull参照であるかどうかは確かです...

  • 後で例外やデータの欠落が発生するため、コードの後にy​​配置できない場合は、不変式が正しくないことがわかっているときに続行するのは単に悪い考えです。null

于 2014-03-07T01:06:09.327 に答える
1

要するに、これは防御的なプログラミングではないと言えます。私は、このコードが公開して修正するのではなく、システム エラーを隠していると考えている人々に同意します。このコードは「フェイル ファスト」の原則に違反しています。

これは、x.parent が必須の非 null プロパティである場合にのみ当てはまります (コンテキストから明らかなようです)。しかし、x.parent がオプションのプロパティである場合 (つまり、合理的に null 値を持つことができる場合)、次のコード表現されるビジネスロジックによっては大丈夫です。

私は常に、大量の無関係な if ステートメントを必要とする null の代わりに、空の値 (0、""、空のオブジェクト) を使用することを検討しています。

于 2014-03-07T22:21:43.747 に答える