一般的な例外をキャッチすることは本当に悪いことでしょうか?

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

  •  09-06-2019
  •  | 
  •  

質問

FXCop を使用してレガシー コードを分析しているときに、try ブロック内で一般的な例外エラーをキャッチするのは本当に悪いことなのか、それとも特定の例外を探すべきなのかが気になりました。ポストカードの感想もお願いします。

役に立ちましたか?

解決

明らかに、これは「状況による」という唯一の本当の答えが得られる質問の 1 つです。

主に依存するのは、どこで例外をキャッチしているかです。一般に、ライブラリはプログラムの最上位レベルでは例外をキャッチすることに関してより保守的であるべきです(例:メインメソッドやコントローラーのアクションメソッドの先頭など) キャッチする内容をより自由に扱うことができます。

この理由は次のとおりです。ライブラリ内のすべての例外をキャッチする必要はありません。ユーザーに通知できるように実際にバブルアップしたい「OutOfMemoryException」など、ライブラリとは関係のない問題をマスクしてしまう可能性があるためです。一方、例外をキャッチし、表示して終了する main() メソッド内で例外をキャッチすることについて話している場合...まあ、ここでほぼすべての例外をキャッチしてもおそらく安全です。

すべての例外をキャッチすることに関する最も重要なルールは、すべての例外を黙って飲み込んではいけないということです...例えばJava では次のようなものです。

try { 
    something(); 
} catch (Exception ex) {}

または Python では次のようになります。

try:
    something()
except:
    pass

これらは追跡するのが最も難しい問題の一部である可能性があるためです。

経験則としては、自分で適切に対処できる例外のみをキャッチする必要があるということです。あなたが例外を完全に処理できない場合は、それができる人に任せるべきです。

他のヒント

アプリケーションのフロントエンドで何らかのロギングやコードのクリーンアップを行っていない限り、すべての例外をキャッチするのは良くないと思います。

私の基本的な経験則は、予期されるすべての例外をキャッチし、それ以外の例外はバグであるということです。

すべてを把握して続行する場合、それは車のダッシュボードの警告灯に絆創膏を貼るようなものです。見えなくなっても、すべてが大丈夫というわけではありません。

はい!(アプリケーションの「上部」を除く)

例外をキャッチしてコードの実行を継続できるようにすることで、特定の問題に対処し、回避または修正する方法を知っていることになります。あなたはこれがそうだと主張しています 回復可能な状況. 。例外または SystemException をキャッチするということは、IO エラー、ネットワーク エラー、メモリ不足エラー、コード欠落エラー、null ポインタの逆参照などの問題をキャッチできることを意味します。これらに対処できるというのは嘘です。

適切に構成されたアプリケーションでは、これらの回復不可能な問題はスタックの上位で処理される必要があります。

さらに、コードが進化するにつれて、追加された新しい例外を関数がキャッチしたくない場合があります。 将来 呼び出されたメソッドに。

私の意見では、すべての例外をキャッチする必要があります 期待する, ただし、このルールはインターフェイス ロジック以外には適用されません。コール スタック全体で、すべての例外をキャッチし、ログを記録したりユーザーにフィードバックを提供したり、必要かつ可能であれば正常にシャットダウンしたりする方法を作成する必要があるでしょう。

ユーザーにとって不親切なスタックトレースが画面にダンプされてアプリケーションがクラッシュすることほど最悪なことはありません。これはコードに関する (おそらく望ましくない) 洞察を与えるだけでなく、エンドユーザーを混乱させ、場合によっては競合するアプリケーションに怖がらせてしまうことさえあります。

この問題については、多くの哲学的な議論(議論に近いもの)が行われてきました。個人的には、最悪のことは例外を飲み込むことだと考えています。次に最悪の事態は、例外が表面に現れることを許可し、ユーザーに専門的な意味不明な内容でいっぱいの厄介な画面が表示されることです。

ポイントは2つあると思います。

まず、どのような例外が発生したかがわからない場合、どのようにしてそこから回復することができるでしょうか。ユーザーがファイル名を間違って入力する可能性があることが予想される場合は、FileNotFoundException が発生することを予期して、ユーザーに再試行するように指示できます。同じコードで NullReferenceException が生成され、単にユーザーに再試行するように指示した場合、ユーザーは何が起こったのかわかりません。

第 2 に、FxCop ガイドラインはライブラリ/フレームワーク コードに重点を置いています。そのルールのすべてが EXE や ASP.Net Web サイトに適用できるように設計されているわけではありません。したがって、すべての例外をログに記録し、アプリケーションを適切に終了するグローバル例外ハンドラーを用意しておくとよいでしょう。

すべての例外をキャッチする場合の問題は、予期しない例外、または実際にキャッチすべき例外をキャッチしてしまう可能性があることです。 ない 捕まえる。実際のところ、いかなる種類の例外も、何か問題が発生したことを示しており、続行する前にそれを解決する必要があります。そうしないと、データの整合性の問題や、追跡が容易ではないその他のバグが発生する可能性があります。

一例を挙げると、あるプロジェクトで CriticalException という例外タイプを実装しました。これは、開発者や管理スタッフによる介入が必要なエラー状態を示します。そうしないと、顧客に誤って請求が発生したり、その他のデータ整合性の問題が発生したりする可能性があります。また、例外をログに記録するだけでは十分ではなく、電子メール アラートを送信する必要がある他の同様のケースでも使用できます。

例外の概念を適切に理解していなかった別の開発者は、この例外をスローする可能性のあるコードを、すべての例外を破棄する汎用の try...catch ブロックにラップしました。幸いなことに、私はそれを発見しましたが、特に、それが検出するはずだった「非常に珍しい」例外的なケースが、私が予想していたよりもはるかに一般的であることが判明したため、深刻な問題を引き起こす可能性がありました。

したがって、一般的に、100% 確実に知っている場合を除き、一般的な例外をキャッチすることは良くありません。 その通り どのような種類の例外が、どのような状況でスローされるか。疑わしい場合は、代わりにトップレベルの例外ハンドラーにバブルアップさせてください。

ここでの同様のルールは、System.Exception 型の例外を決してスローしないことです。あなた (または他の開発者) は、他の開発者を通過させながら、コール スタックの上位で特定の例外をキャッチしたい場合があります。

(ただし、注意点が1つあります。.NET 2.0 では、スレッドでキャッチされなかった例外が発生すると、アプリ ドメイン全体がアンロードされます。したがって、スレッドの本体を汎用の try...catch ブロックでラップし、そこでキャッチされた例外をグローバル例外処理コードに渡す必要があります。)

そうですね、一般的な例外をキャッチすることと特定の例外をキャッチすることの間に違いはありませんが、複数の catch ブロックがある場合、例外の内容に応じて異なる反応ができる点が異なります。

結論から言うと、両方とも釣れます IOException そして NullPointerException ジェネリックで Exception, ですが、プログラムが反応すべき方法はおそらく異なります。

私は例外をキャッチしてログに記録し、再スローするという悪魔の擁護者を演じたいと考えています。これは、たとえば、コードのどこかで予期しない例外が発生した場合に必要になることがあります。これをキャッチして、単純なスタック トレースでは取得できない意味のある状態情報をログに記録し、それを上位層に再スローして、対処する。

まったく異なる 2 つの使用例があります。1 つ目は、ほとんどの人が考えているもので、チェック例外を必要とする操作の周囲に try/catch を配置するものです。これは決して包括的なものであってはなりません。

ただし 2 つ目は、プログラムが継続できるときにプログラムが中断されるのを防ぐことです。これらのケースは次のとおりです。

  • すべてのスレッドの先頭 (デフォルトでは、例外は跡形もなく消えます!)
  • 決して終了しないと予想されるメイン処理ループ内
  • 1 つの障害が他の障害を停止させてはならないオブジェクトのリストを処理するループ内
  • 「メイン」スレッドの先頭 -- メモリが不足したときに少量のデータを標準出力にダンプするなど、ここでクラッシュを制御できます。
  • コードを実行する「ランナー」がある場合 (たとえば、誰かがリスナーを追加し、そのリスナーを呼び出した場合)、コードを実行するときに例外をキャッチして問題をログに記録し、他のリスナーに引き続き通知できるようにする必要があります。

このようなケースでは、プログラミングや予期しないエラーを捕捉し、ログに記録して続行するために、常に例外 (場合によっては Throwable さえも) を捕捉する必要があります。

良いガイドラインは、フレームワーク内から特定の例外のみをキャッチすることだと思います(ホスト アプリケーションがディスクがいっぱいになるなどのエッジ ケースに対処できるようにするため)。しかし、なぜすべてをキャッチできないのかわかりません。アプリケーションコードの例外。簡単に言えば、何が問題を起こしてもアプリをクラッシュさせたくない場合があります。

ほとんどの場合、一般例外をキャッチする必要はありません。もちろん選択の余地がない状況もありますが、この場合はなぜ捕まえる必要があるのか​​を確認したほうが良いと思います。もしかしたら設計に何か問題があるのか​​もしれません。

一般的な例外を捉えることは、燃えている建物の中でダイナマイトの棒を持ち、信管を消すようなものだと思います。一時的には役に立ちますが、しばらくするとダイナマイトが爆発します。

もちろん、一般的な例外のキャッチが必要な状況もあるかもしれませんが、それはデバッグ目的のみです。エラーやバグは隠すのではなく、修正する必要があります。

アプリ内課金で使用した IabManager クラス (オンラインの TrivialDrive サンプルから) では、多くの例外を処理する場合があることに気付きました。予測不可能な状況になってしまいました。

1 つの例外が発生した後で、アプリ内製品を消費しようとする試みをやめれば、ほとんどの例外が発生します (購入ではなく消費で) が安全であることに気づきました。

すべての例外を一般例外に変更しただけです。これで、他のランダムで予測不可能な例外がスローされることを心配する必要がなくなります。

前に:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

後:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

不人気な意見:あまり。

有意義に回復できるすべてのエラーを捕捉します。時にはそれがすべてです。

私の経験では、それはより重要です どこ 例外が実際にスローされる例外よりも発生元です。例外を狭い場所に保管しておけば、通常は役に立つはずのものを飲み込むことはありません。エラーのタイプにエンコードされた情報のほとんどは補助情報であるため、通常は効果的にエラーを捕捉することになります。 全て とにかくそのうちの 1 つです (ただし、考えられる例外の合計セットを取得するには、API ドキュメントを参照する必要があります)。

Python のような例外は、ほぼすべての場合で先頭に表示されることに注意してください。 KeyboardInterrupt そして SystemExit. 。Python の場合、幸いなことに、これらは例外階層の別のブランチに保持されるため、キャッチすることでバブルアップさせることができます。 Exception. 。適切に設計された例外階層により、この種の処理が非常に簡単になります。

一般例外をキャッチすることで深刻な問題が発生する主な原因は、クリーンアップが必要なリソースを処理するときです (おそらく、 finally 句)、キャッチオールハンドラーはそのようなことを簡単に見逃してしまう可能性があるためです。幸いなことに、これは次の言語では実際には問題ではありません。 defer, 、Python のような構造 with, 、または C++ および Rust の RAII。

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