web-dev-qa-db-ja.com

エラー抑制に対する議論

私たちのプロジェクトの1つで、次のようなコードを見つけました。

    SomeClass QueryServer(string args)
    {
        try
        {
            return SomeClass.Parse(_server.Query(args));
        }
        catch (Exception)
        {
            return null;
        }
    }

私が理解している限り、このようなエラーを抑制することは悪い習慣です。元のサーバーの例外から有用な情報が破棄され、実際に終了する必要があるときにコードが続行されるためです。

このようなすべてのエラーを完全に抑制することはいつ適切ですか?

34
user626528

たくさんのライブラリを使用して数千のファイルを含むコードを想像してください。それらすべてがこのようにコード化されていると想像してください。

たとえば、サーバーを更新すると、構成ファイルが1つ消えてしまうとします。そして今あなたが持っているのは、そのクラスを使用しようとしたときのスタックトレースがnullポインタ例外です:それをどのように解決しますか?数時間かかる場合があります。少なくとも[ファイルパス]が見つからないファイルの生のスタックトレースをログに記録するだけで、一時的に解決できる場合があります。

またはさらに悪いことに、更新後に使用するライブラリの1つに障害が発生し、後でコードがクラッシュします。これをライブラリに追跡するにはどうすればよいですか?

堅牢なエラー処理がなくても、

throw new IllegalStateException("THIS SHOULD NOT HAPPENING")

または

LOGGER.error("[method name]/[arguments] should not be there")

時間を節約できます。

ただし、例外を無視して、このようにnullを返したい(または何もしない)場合もあります。特に、不適切に設計されたレガシーコードと統合し、この例外が通常のケースとして予想される場合。

実際、これを行うときは、本当に例外を無視しているのか、またはニーズに合わせて「適切に処理」しているのか疑問に思うだけです。その特定のケースでnullを返すことが例外を「適切に処理」している場合は、それを実行します。そして、これが適切な理由である理由をコメントに追加します。

ベストプラクティスはほとんどの場合、おそらく80%、99%で従うべきものですが、適用されないEdgeケースが必ず1つあります。その場合は、コメントを残して、数か月後にコードを読む他の人(または自分自身)のプラクティスに従っていない理由を記入してください。

52
Walfrat

このパターンが役立つ場合がありますが、それらは通常、生成された例外が本来あるべきではない場合(つまり、例外が通常の動作に使用される場合)に使用されます。

たとえば、ファイルを開き、返されたオブジェクトに保存するクラスがあるとします。ファイルが存在しない場合は、エラーではないと考え、nullを返し、代わりにユーザーに新しいファイルを作成させることができます。 file-openメソッドは、ファイルが存在しないことを示すためにここで例外をスローする可能性があるため、黙ってそれをキャッチすることは有効な状況である可能性があります。

ただし、これを実行することはあまりありません。そのようなパターンがコードに散らばっている場合は、今すぐ処理する必要があります。少なくとも、このようなサイレントキャッチは、これが何が起こったのか(トレースがあります)であることを示すログ行と、この動作を説明するコメントを書き込むことを期待します。

14
gbjbaanb

これは100%コンテキスト依存です。そのような関数の呼び出し元がどこかにエラーを表示または記録する必要がある場合、それは明らかにナンセンスです。呼び出し元がすべてのエラーまたは例外メッセージも無視した場合、例外またはそのメッセージを返すことはあまり意味がありません。理由を表示せずにコードのみを終了させることが要件である場合は、呼び出し

   if(QueryServer(args)==null)
      TerminateProgram();

おそらく十分でしょう。これにより、ユーザーによるエラーの原因の特定が困難になるかどうかを検討する必要があります。その場合、これはエラー処理の間違った形式です。ただし、呼び出しコードが次のようになっている場合

  result = QueryServer(args);
  if(result!=null)
      DoSomethingMeaningful(result);
  // else ??? Mask the error and let the user run into trouble later

次に、コードのレビュー中に開発者と議論する必要があります。誰かが怠惰すぎて正しい要件を見つけることができないという理由だけで、このような形式の非エラー処理を実装する場合、このコードは本番環境に入れるべきではなく、コードの作成者はそのような怠惰の起こり得る結果について教えられるべきです。

7
Doc Brown

私がシステムのプログラミングと開発に費やしてきた数年で、問題のパターンが有用であるとわかったのは2つの状況だけです(どちらの場合も、抑制にはスローされた例外のロギングも含まれていたため、プレーンキャッチとnull returnは適切とは見なしていません練習)。

2つの状況は次のとおりです。

1.例外が例外的な状態と見なされなかった場合

これは、スローする可能性のある一部のデータに対して操作を行う場合であり、スローする可能性があることはわかっていますが、処理されたデータは必要ないため、アプリケーションを引き続き実行したい場合があります。それらを受け取った場合、それは良いことであり、受け取っていない場合、それもまた良いことです。

クラスのいくつかのオプション属性が頭に浮かぶかもしれません。

2.アプリケーションですでに使用されているインターフェイスを使用して、ライブラリの新しい(より良い、より速い?)実装を提供する場合

ある種の古いライブラリを使用するアプリケーションがあり、例外をスローしなかったがエラーでnullを返したとします。したがって、このライブラリのアダプタを作成し、ライブラリの元のAPIをほとんどコピーし、アプリケーションでこの(まだスローされない)インターフェースを使用して、nullチェックを自分で処理しています。

新しいバージョンのライブラリ、またはおそらく同じ機能を提供する完全に異なるライブラリが登場します。これは、nullsを返す代わりに例外をスローし、それを使用したいとします。

例外をメインアプリケーションにリークしたくないので、この新しい依存関係をラップするために、作成したアダプターに例外を抑制してログに記録します。


最初のケースは問題ではなく、コードの望ましい動作です。ただし、2番目の状況では、ライブラリアダプターのnull戻り値が実際にエラーを意味する場合は、nullをチェックする代わりにAPIをリファクタリングして例外をスローし、キャッチすることをお勧めします(通常、コード的にはそうです)。 。

私は個人的に例外抑制を最初のケースにのみ使用します。私は、nullsの代わりに例外を使用して残りのアプリケーションを機能させるための予算がなかった2番目のケースでのみ使用しました。

5
Andy