web-dev-qa-db-ja.com

サードパーティのライブラリによって吸収される例外を処理するにはどうすればよいですか?

現在、サードパーティのコントロールライブラリプロバイダーに問題があります。彼らには、ソフトウェアを開発する際の私の一般的なフェイルファストアプローチの邪魔になる例外的なオカルト文化があります。

例:グリッドコントロールはRowValidatingイベントを提案します。このイベントでは、基になるオブジェクトにコミットされる前にユーザーデータ入力を確認できます。イベント引数には、IsValidブールプロパティとErrorText文字列プロパティがあります。ハンドラーがIsValidプロパティをFalseに設定している場合、グリッドは行を無効として扱い、エラーテキストの後に「値を修正しますか?」と表示します。はい/いいえダイアログボックスで。

問題は、検証プロセス中にハンドラーが実際のExceptionをスローした場合、グリッドがそれをキャッチし、上記とまったく同じように動作することです。キャッチされた例外のMessageプロパティを検証として使用するだけです。エラーテキスト。例外インスタンスは、実際にはそれを引き起こしたのはMYコードですが、後でキャッチすることはできません。すべてをキャッチするtry/catchブロックで検証コードをラップする以外、それを処理する方法はありません。

もちろん、コードで例外をスローしたくありませんが、ちょっと...errare humanum est!それが発生した場合、私は 例外を非表示にするよりも、アプリをクラッシュさせて書き込む !これらのシナリオでは、アプリがクラッシュする直前に未処理の例外をログに記録するグローバルハンドラーがあります。これは私がそれらのいずれかが終わることを期待するところです。

私の会社はこの特定のサードパーティプロバイダー(学習と実際のコードの両方から)に多大な投資を行ったので、別のベンダーに実行することはできません。

これが修正されるようにベンダーに話してみましたが、他の顧客に重大な変更を引き起こすことを恐れて何もしません(理解できる)。また、動作を回避するためのブールフラグは導入されません(あまり理解できません)。

現時点では、アプリが破損した状態で実行される可能性があることがわかっているアプリをデプロイするのは不快です。また、イベントコードをExceptionに遭遇する可能性のあるtry/catchブロックでラップする必要があり、そこから正常に回復することはできません。

この問題を防止または修正するには、どのようなソリューションを使用できますか?

TL; DR:サードパーティベンダーが自分のコードからスローされたExceptionインスタンスを取り除きます。回避策は醜く、満足のいくものではありません。これをどうするか。

6
Crono

最近、サードパーティのライブラリで同様の問題を解決しました。私があなたの状況を誤解していないことを確認するために、言い直してください。あなたはそれを回避する方法を知っていますが、回避策の繰り返しが好きではなく、それが実際のコードを覆い隠していると感じますか?

例外をキャッチして適切に処理するpythonデコレータを使用して問題を解決しました。これにより、関数定義に対する追加の@handle_errorsは1つだけで、かさばるtry-catchブロックではありません.net製品ラインについてはあまり詳しくありませんが、取得できるアスペクト指向のプログラミングライブラリがあるか、または 装飾パターン ボイラープレートを同様に削減します。

残念ながら、それ以上のことはできません。それはベンダーロックインの価格にすぎません。しかし、これは確率が低く、影響が少ない状況のようです。検証ルーチンはほとんどステートレスであり、徹底的にテストするのは比較的簡単なはずです。また、例外がユーザーに表示されても、その例外はほとんど「隠され」ません。これは、Jeff Atwoodのブログで説明されている状況ではありません。

2
Karl Bielefeldt

.NETフレームワークの標準UIコントロールは、未処理の例外を単独でキャッチせず、それらの例外を中央でキャッチするメカニズムを提供するため、サードパーティのコントロールの動作が異なる理由が疑わしいことに同意します。

アプリケーションの性質上、重大な障害が発生した場合、任意のGUIイベントであっても、一貫性のない大量のデータを取り戻すリスクなしに、いつでもアプリケーションを安全に終了できると100%確信できます。次に、次のような関数ラッパーを作成することを検討します。

public class MyExit
{
  public void WhenUnhandledException(Action action)
  {
    try
    {
        action();  
    }
    catch(Exception ex)
    {
         // ... do some additional logging, if you like ...
         Environment.FailFast(ex.Message+"\n"+ex.StackTrace);
    }
  }
}

次のようなコード全体でこの関数を使用します。

  void MyGUIExceptionHandler(object o, EventArgs e)
  {
     MyExit.WhenUnhandledException(() => {
       // add code, maybe using o and e
     });
  }

そうすれば、同じtry/catch/log/FailFastコードを2回以上繰り返す必要がなくなり、必要に応じて後でフェイルファストとロギングの戦略を変更できます。

5
Doc Brown

何をすべきか。私の見解では、検証中に例外を失敗した検証に変換する決定は、そのような例外を処理する正しい方法です。

  • 例外が通過してアプリケーションがクラッシュすると、たとえユーザーの入力が正しくなく、完全に修正可能であっても、ユーザーが行っていた作業が失われるという重大なリスクがあります。
  • 検証ルーチンでは、入力が有効であるかどうかのみを確認し、notでアプリケーションの状態を変更する必要があります。したがって、検証ルーチンで例外がスローされた場合、アプリケーションの状態が破損するリスクはゼロです。
    ハンドラーで検証だけを行うのではない場合、無効なアプリケーション状態でそこからパスが出ていないことを確認することが問題です。

私はハードエラーが非常に好きで、すぐに失敗します。それらは真実で正しい方法の1つだと思いますが、私はそれらについて独断的にならないようにします。

予期しない例外を処理する最善の方法は、ログに記録してそれを飲み込むことです。状況よりも簡単な例を紹介します。変更されたときに、登録されたオブザーバーのリストに通知を発行するオブザーバブルコレクションがあるとします。そして、オブザーバーの1人が例外をスローするとします。コレクションのせいですか?いいえ、誰が登録するかは制御できません。コレクションを変更したのはコードのせいですか?確かにそうではありません。そのコードは、変更しているコレクションが監視可能なものであることをおそらく認識していませんでした。登録され、通知を受け取ることを期待している残りのオブザーバーの責任ですか?また、いいえ。それぞれが他のオブザーバーを認識しておらず、誰に最初に通知するかを制御できません。したがって、監視可能なコレクションで実行できることは、例外をログに記録してそれを飲み込み、何も起こらなかったかのようにリストの残りのオブザーバーを呼び出し続けることだけが理にかなっています。

したがって、そのライブラリの作成者は同様の状況にあり、同じ決定を行わなければなりませんでした。

私があなたの立場で行うのは、私を呼び出すライブラリに常に意味のあるエラーメッセージが返されるように、すべてのエントリポイントをtry-catchで囲むことです。

そのようなことをする言語によるサポート(例外をキャッチしてオーバーライド可能オブジェクトを呼び出してそれらを処理する自動生成されたデコレーター)をサポートすることは素晴らしいことであり、カールビーレフェルトの答えは、pythonにはそのようなメカニズムはありますが、私たちが自由に使用できるそのような優れた機能を持っていない私たちは、手作業でそれを行うことにこだわっています。

編集:

致命的ではない例外が発生したことを開発者が知る必要性は、例外をログに記録することでほぼカバーされます。 (アプリケーションのデバッグ中にログを監視しませんか?)

さて、私のように、致命的ではない例外がログによってスクロールされて気付かれない可能性を排除したい場合は、私が行うことを実行できます。

例外を飲み込むための集中化されたユーティリティメソッドがあり、例外をログに記録した直後に、次の非常に便利な呼び出しを実行します。

System.Diagnostics.Debugger.Break();

現場では、これは何もしません。しかし、開発環境では(デバッガーの下で実行中)、これはデバッガーに割り込むため、理論的にはマシンの状態を調べて例外の原因に関する詳細情報を得ることができますが、さらに重要なことは、例外がスローされたことに気づきました。

もちろん、これはあなたのアプリケーションを開発している間は決してそれを実行するのではなく、常にデバッグすることを前提としています。これが正しい動作モードです。万が一その癖が無い方は是非入手して頂きたいと思います。

2
Mike Nakis