エラー処理-プログラムがエラーで失敗するか、黙って無視するか


20

ネットワーク経由でMIDIを送信するための簡単な小さなプログラムを書いています。私は、プログラムが伝送の問題や予測できない他の例外状況に遭遇することを知っています。

例外処理については、2つのアプローチがあります。次のようにプログラムを作成する必要があります。

  • 何かがうまくいかないときは強打で失敗する か、
  • データの整合性を犠牲にして、エラーを無視して続行する必要がありますか?

ユーザーはどのアプローチを合理的に期待しますか?
例外を処理するより良い方法はありますか?

さらに、例外の処理に関する私の決定は、ネットワーク接続を処理しているかどうか(つまり、問題が発生することが合理的に予想できるもの)に影響されるべきですか?


1
GlenH7 @ ペイ前方に... :)
ブヨ

編集を+1する方法はありますか?;)ああ、待って、私はあなたに従います。確かに、するでしょう:)
アーレンベイラー

2
「ユーザーが合理的に期待するアプローチはどれですか?」。ユーザーの1人に聞いてみましたか?廊下のユーザビリティテストは非常に効果的です。参照してくださいblog.openhallway.com/?p=146
ブライアンオークリー

ユーザーはいません:)
アーレンベイラー

回答:


34

プログラムで発生したエラーを決して無視しないでください。最低限、通知のためにファイルまたはその他のメカニズムにログを記録する必要があります。そこあなたがエラーを無視ますがしたいと思うでしょう時々の状況も、それを文書化!空であるcatch理由を説明するコメントなしで空のブロックを記述しないでください。

プログラムが失敗するかどうかは、コンテキストに大きく依存します。エラーを適切に処理できる場合は、先に進んでください。予期しないエラーの場合、プログラムはクラッシュします。これが例外処理の基本です。


18
-1を保証するのに十分ではありませんが、「決して」は強力な言葉ではなく、エラーを無視することが正しい行動である状況があります。たとえば、ブロードキャストHDTV伝送をデコードするソフトウェア。頻繁に発生するいくつかのエラーが発生した場合、できることはそれを無視し、その後に続くものをデコードし続けることだけです。
-whatsisname

1
@whatsisname:true。ただし、なぜ無視するのを明確に文書化する必要があります。コメントを検討するために回答を更新しました。
marco-fiset

1
@ marco-fiset同意しない。プログラム全体をクラッシュさせることは、再起動することで問題が解決する場合にのみ解決策となります。これは常にそうであるとは限らないので、それをクラッシュさせることはしばしば無意味であり、その振る舞いは単純に愚かです。ローカライズされたエラーのために、操作全体を一時停止するのはなぜですか?それは意味がありません。ほとんどのアプリケーションはそれを行いますが、何らかの理由で、プログラマーはそれでまったく問題ありません。
-MaiaVictor

@Dokkat:ポイントは間違った値で続行しないことです。これは間違った解決策を提供します(ガベージイン、ガベージアウト)。クラッシュは問題を解決します。ユーザーに異なる入力を提供するか、開発者にプログラムの修正を
せがむように強制し

1
@whatsisnameその場合の「エラー」の定義について考えたいと思うかもしれません。受信したデータのエラーは、ソフトウェアプログラムの構造と実行のエラーとは異なります。
joshin4colours

18

プログラムは一連のアクションに基づいて構築されているため、エラーを黙って無視することは絶対にしないでください。ステップ3で問題が発生し、ステップ4に進むと、無効な仮定に基づいてステップ4が開始されるため、エラーが発生する可能性が高くなります。(それも無視すると、ステップ5でエラーがスローされ、そこから雪だるま式が始まります。)

問題は、エラーが積み重なると、最終的にはユーザーに与えられた何かで構成され、何かが完全に間違っているため、無視できないほど大きなエラーが発生することです。それから、あなたのプログラムが正しく動作しないと不満を言うユーザーがいて、それを修正しなければなりません。そして、「ユーザーに何かを与える」部分がステップ28にあり、ステップ3でエラーを無視したために、この混乱を引き起こしている元のエラーがステップ3にあったことを知らない場合、問題をデバッグするのに時間がかかります!

一方、ステップ3のエラーにより、ユーザーの顔にすべてが吹き飛ばされ、SOMETHING WENT BADLY WRONG IN STEP 3!(または技術的に同等の、スタックトレース)というエラーが生成される場合、結果は同じです-ユーザーはあなたに不平を言っていますプログラムは正常に動作していませんが、今回は修正するときにどこから探し始めるかを正確に知っています

編集:コメントへの応答で、あなたが予想し、対処方法を知っている何かがうまくいかない場合、それは異なります。たとえば、不正な形式のメッセージを受信した場合、それはプログラムエラーではありません。それは「ユーザーが検証に失敗した不適切な入力を提供した」ということです。そこでの適切な応答は、無効な入力を与えていることをユーザーに伝えることです。これは、あなたがしているように聞こえます。そのような場合にクラッシュしてスタックトレースを生成する必要はありません。


そのメッセージをドロップして次のメッセージに進むために、最後の既知の適切な位置に戻る方法、または受信ループの場合はどうでしょうか。
アーレンベイラー

@アーレン:それでも悪い考えです。エラーが発生するのは、コーディング中に予期しなかっ何かが発生したためです。これは、すべての前提が窓から外れたことを意味します。たとえば、データ構造の変更の途中で、たとえば一貫性のない状態になっている場合、その「最後の既知の良い位置」はもはや良くないかもしれません。
メイソンウィーラー

破損したメッセージが逆シリアル化を妨げているように、それを期待している場合はどうなりますか。場合によっては、例外をシリアル化し、ネットワーク経由で送り返しました。そこで、質問に対する私の編集を参照してください。
アーレンベイラー

@Arlen:予想していた場合、エラーの影響が何であるかがわかっていて、エラーが適切に含まれていると確信している場合、それは異なります。エラーを無視しているのではなく、エラーを処理して適切に応答しているように聞こえます。私は予期ないエラーを無視することについて話していました。それはあなたが尋ねているように聞こえたものです。
メイソンウィーラー

16

「爆発」と「無視」の間に他のオプションがあります。

エラーが予測可能で回避可能な場合は、設計を変更するか、コードをリファクタリングして回避してください。

エラーが予測可能であるが回避できない場合、エラーが発生した場合の対処方法がわかっている場合は、エラーをキャッチして状況を処理します。ただし、フロー制御として例外を使用しないように注意してください。そして、この時点で警告をログに記録し、将来この状況を回避するために実行するアクションがある場合は、ユーザーに通知することができます。

エラーが予測可能で避けられないものであり、発生した場合にデータの整合性を保証することができない場合は、エラーをログに記録し、安全な状態にフォールバックする必要があります(他の人が言っているように、クラッシュする可能性があります)。

エラーが予期したものでない場合、安全な状態にフォールバックできるかどうかは本当に確信できないので、ログに記録してクラッシュするのが最善かもしれません。

一般的なルールとして、ログに記録して再スローするだけでない限り、何もできない例外をキャッチしないでください。また、try-catch-ignoreが避けられないまれなケースでは、少なくともcatchブロックにコメントを追加して理由を説明してください。

例外の分類と処理に関するその他の提案については、Eric Lippertの優れた例外処理の記事を参照してください。


1
私の意見では、これまでのベストアンサー。
木曜日

6

これらは質問に対する私の見解です:

良い開始原則は、早く失敗することです。具体的には、正確な原因がわからない障害については、エラー処理コードを記述しないでください。

この原則を適用した後、発生した特定のエラー状態の回復コードを追加できます。戻るためのいくつかの「安全な状態」を導入することもできます。プログラムの中止はほとんど安全ですが、時には別の既知の良好な状態に戻りたい場合があります。例として、最新のOSが問題のあるプログラムを処理する方法があります。OS全体ではなく、プログラムをシャットダウンするだけです。

より多くの特定のエラー状態を迅速かつゆっくりとカバーすることにより、データの整合性を損なうことなく、より安定したプログラムに着実に移行できます。

エラーを飲み込む、つまり、正確な原因がわからないため特定の回復戦略を持たないエラーを計画しようとすると、エラースキップの量が増え、プログラム内のコードが回避されます。以前のデータが正しく処理されたことを信頼することはできないため、誤ったデータまたは欠落したデータのチェックを広げることができます。あなたのサイクロマティックな複雑さは手に負えずらせん状になり、泥の大きな玉になってしまいます。

失敗の事例を認識しているかどうかは、それほど重要ではありません。ただし、一定量のエラー状態がわかっているネットワーク接続を処理している場合は、回復コードも追加するまでエラー処理の追加を延期してください。これは上記の原則に沿っています。


6

エラーを黙って無視してはなりません。特に、データの整合性を犠牲にすることはありません。

プログラムは何かをしようとしています。それが失敗した場合、あなたは事実に直面し、それについて何かをしなければなりません。それが何になるかは、多くのことに依存します。

最終的に、ユーザーはプログラムに何かを行うよう要求し、プログラムは成功しなかったことを伝える必要があります。それを行う方法はたくさんあります。すぐに停止したり、すでに完了したステップをロールバックしたり、可能な限りすべてのステップを続行して完了したり、これらのステップが成功し他のステップが失敗したことをユーザーに伝えることができます。

どちらの方法を選択するかは、ステップがどの程度密接に関連しているか、および将来のすべてのステップでエラーが再発する可能性が高いかどうかによって決まります。強力なデータ整合性が必要な場合は、最後の一貫した状態にロールバックする必要があります。大量のファイルをコピーする場合は、一部をスキップして、最後にそれらのファイルをコピーできなかったことをユーザーに伝えることができます。静かにファイルをスキップして、ユーザーに何も言わないでください。

広告編集の唯一の違いは、ネットワークに一時的なエラーがあり、再試行しても再発しない可能性があるため、あきらめてユーザーにそれが機能しなかったことを伝える前に、何度か再試行することを検討する必要があることです。


本当に最初の行の最後に疑問符を付けるつもりでしたか?
10:24のCVn

@MichaelKjörling:いいえ。コピーペーストエラー(質問から定式化をコピーし、誤って疑問符を含めました)。
ジャン・ヒューデック

4

エラーを無視することが正しいことである場合の1つのクラスがあります。状況に関しておそらく実行できることは何もないとき、そして貧弱で、おそらく誤った結果が結果なしよりも良いときです。

表示目的でHDMIストリームをデコードする場合はこのような場合です。ストリームが悪い場合、それは悪いです、それについて叫ぶことは魔法のようにそれを修正しません。あなたはそれを表示するためにできることをし、それが許容できるかどうかを視聴者に決めさせます。


1

プログラムが問題に遭遇するたびに静かに無視したり大混乱を引き起こしたりするとは思わない。

会社のために書いた社内ソフトウェアで私がしていること...

エラーに依存します。MySQLにデータを入力するのが重要な機能である場合、ユーザーにエラーが発生したことを知らせる必要があります。エラーハンドラーは、できるだけ多くの情報を収集し、ユーザーがデータを保存できるように自分で間違いを修正する方法のアイデアを提供しようとする必要があります。また、保存しようとしている情報を静かに送信する方法を提供したいので、さらに悪化した場合は、バグを修正した後に手動で入力できます。

それが重要な機能ではなく、エラーを引き起こし、達成しようとしているものの最終結果に影響を与えないものであれば、エラーメッセージを表示せず、バグ追跡ソフトウェアに自動的に挿入する電子メールを送信しますまたは、社内のすべてのプログラマーに警告する電子メール配布グループ。これにより、ユーザーがそうでない場合でもエラーを認識できます。これにより、フロントエンドでは何が起こっているのか誰も知らないうちに、バックエンドを修正できます。

私が回避しようとする最大のことの1つは、エラー後にプログラムがクラッシュすることです-回復できません。私は常に、ユーザーにアプリケーションを閉じずに続行するオプションを提供しようとします。

バグについて誰も知らない場合、私は信じています-それは決して修正されません。また、バグが発見されてもアプリケーションが機能し続けることを可能にするエラー処理を固く信じています。

エラーがネットワーク関連である場合-そもそもエラーを回避するために、関数を実行する前に単純なネットワーク通信テストを関数で実行しないのはなぜですか?次に、接続が利用できないことをユーザーに警告するだけで、インターネットなどを確認してからもう一度お試しください。


1
私は、重要な機能を実行する前に個人的に多くの検証を行い、完全には理解できず、詳細な情報を返す有用なエラー処理を提供できないエラーを制限しようとします。100%動作するわけではありませんが、最後に何時間も失われたバグがあったことを思い出せません。
ジェフ

1

私自身の戦略は、コーディングエラー(バグ)とランタイムエラー区別し、可能な限りコーディングエラーの作成を困難にすることです。

バグはできるだけ早く修正する必要があるため、契約による設計アプローチが適切です。C ++では、できるだけ早くバグを検出し、デバッガーの接続とバグの修正を容易にするために、関数の上部にあるアサーションですべての前提条件(入力)をチェックするのが好きです。開発者またはテスターが代わりにプログラムの実行を続けることを選択した場合、データの整合性の損失が問題になります。

そして、そもそもバグを防ぐ方法を見つけてください。const-correctnessを厳しくし、保持するデータに適したデータ型を選択することは、バグの作成を困難にする2つの方法です。Fail-Fastは、復旧する方法を必要とする安全性が重要なコード以外にも適しています。

以下のためのランタイムエラーおそらくバグのない、ネットワークまたはシリアル通信障害などのコード、または欠落または破損したファイルで発生する可能性があります:

  1. エラーを記録します。
  2. (オプション)操作をサイレントに再試行するか、その他の方法で回復します。
  3. 操作が引き続き失敗するか、回復できない場合は、エラーをユーザーに視覚的に報告します。その後、上記のように、ユーザーは何をするかを決定できます。事前に警告していない限り、データの整合性が失われることはユーザーにとって驚くべきことであるため、最小の驚きの原則を覚えておいてください。

0

プログラムの全体的な状態が不安定であり、これから実行を続けると何か悪いことが起こると考える理由がある場合、失敗は正しい選択肢です。ある程度「無視」(つまり、他の人が指摘したように、どこかにログを記録するか、ユーザーにエラーメッセージを表示してから続行する)は、確かに現在の操作を実行できないことを知っていれば問題ありませんが、プログラムは走り続ける。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.