空のcatchステートメントを使用しても大丈夫ですか?


58

私はそれについて考え、例を思い付くことができませんでした。なぜ誰かが例外をキャッチし、それについて何もしたくないのでしょうか?例を挙げていただけますか?たぶんそれは決してやるべきではない何かです。


最終的にはどうですか?
クリス

2
ところで-これは昨年SOで尋ねられました:stackoverflow.com/questions/1234343/…–
ウォーレン

17
少なくとも空である理由のコメントを含める必要があります。
ピーターチェン

回答:


77

私はいつもDの変換エラーのようなことでそれをしています:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

とはいえ、例外を無視している理由を説明する単なるコメントであっても、すべてのcatchブロックに何かが含まれている必要があると思います。

編集:また、このようなことをする場合は、無視したい特定の例外のみをキャッチするように注意する必要があることを強調したいと思います。昔ながらのことをすることcatch {}は、ほとんど常に悪いことです。


15
説明コメントの場合は+1。
マイケルK

一部のIDEでは、例外の特定の名前(通常は「無視」)が意図的にそれを無視していることを示すように指定することができます。これがコメントの代わりになります。
アレックスファインマン

私は馴染みのないDですが、bool TryParse(string line、out double valToParse)のようなことができると思います。
ysolik

4
うわー、実際にDを使用している人!:-P
ジェームズMcNellis

4
@Michaelが言っているように- コメントがあるので空ではない。ステートメントがない場合は、「このキャッチを意図的に空白のままにする」コメントを追加する必要があります
-STW

50

何もせず、例外を記録するだけで例外を飲み込むことができると私が思う一例は、コード自体を記録することです。

何かをログに記録しようとして例外が発生した場合、それに対してできることはあまりありません。

  • もちろんログに記録することはできません。
  • リザーブロギングメカニズムにフォールバックできる場合もありますが、ほとんどのアプリケーションはそれほど洗練されておらず、十分に洗練されているアプリケーションでは、リザーブロギングメカニズムで同じ設計上の問題が発生します。
  • 迅速な失敗-ほとんどのアプリケーションにとって過激すぎるソリューションです。
  • 例外をスローすると、ほとんど確実にログを記録しようとするスタックのcatchステートメントで終了するため、ほとんど役に立ちません...

これにより、アプリケーションがメインジョブを実行し続けることができますが、それをトラブルシューティングする機能を損なう「静かに飲み込む」オプションが残ります。


10
素晴らしい点。デバッグコードのデバッグは常に課題です。
DevSolo

7
これ、百万回。特に、ログを記録している場合、ひどい状態になっている可能性があることを考慮すると、メモリー不足、クラッシュ、その他何でもかまいません。この時点で、エラーをログに記録していて、それが失敗した場合、行うべき唯一の責任は何もありません。試してみても悪化しないという保証はありません。
GWLlosa

素晴らしい点。ロギングコードでは、例外のInnerMessageも追加します。多くの場合、これはnullであるため、追加しようとするとロギング自体が爆破されます。
タングレナ

@Tangurenaそれがしばしばヌルであるなら、それを処理してください、吹き飛ばさないでください。C#では、よくこのようなことを??でガードします。「」;
ローレンペクテル

8

とにかくオプションであった操作によって例外がスローされた場合、何もする必要はありません。ログに記録しても役に立たない場合があります。その場合、あなたはそれを捕まえて何もしないことを望むかもしれません。

これを行う場合は、コメントを含めてください。バグのように見えるものは常にコメントしてswitchください(ステートメント内のフォールスルー、空のcatchブロック、条件内の割り当て)。

これは例外の適切な使用法ではないと主張するかもしれませんが、それは別の質問です。


6

空のcatchブロックは、ほとんどの言語でコードの匂いがします。主なアイデアは、例外を例外的な状況に使用し、例外を論理制御に使用しないことです。すべての例外はどこかで処理する必要があります。

このアプローチを採用した結果、特定のアプリケーション層をプログラムする場合、いくつかの選択肢があります。

  • 例外については何もしません。別のレイヤーでキャッチされ、処理されます
  • それをキャッチして、修正アクションを実行します。
  • それを捕まえて何かをするが、別の層が処理するためにそれを再投げる

これは、何もしない、空のcatchブロックの余地を実際に残しません。

追加の編集:例外をスローすることが通常のプログラムロジックの制御方法である言語でプログラミングしているとします(の代替手段の1つgoto)。そして、空catch、この言語で書かれたプログラムでのブロックは非常に多くの空のようなものでelse、伝統的な言語(または全く内のブロックelseのすべてのブロック)。ただし、これはC#、Java、またはC ++開発コミュニティが推奨するプログラミングスタイルではないと考えています。


ロジック制御として例外を使用することはかなり一般的になったと思うので、空またはほぼ空のcatchブロックが頻繁にあることに気付きます。
-whatsisname

空のcatchブロックフロー制御の例外の使用を示している可能性があることを認識するための+1 。
ジョンMガント

プログラミングロジックに例外を使用することは、一般的に悪い習慣と見なされます。しかし、驚くべきことに(例外に対する私の元の議論)例外は、目立ったパフォーマンスヒットを被りません。codeproject.com/KB/exception/ExceptionPerformance.aspxを参照してください。
エヴァンプライス

6

場合によっては、Javaでは、絶対にまったく発生することのない例外を処理する必要があります。次のコードを検討してください。

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Javaプラットフォームのすべての実装は、次の標準文字セットをサポートする必要があります。

...

UTF-8

Javaプラットフォームを壊さなければ、この例外を引き起こす方法はありません。なぜそれを処理するのが面倒なのでしょうか?ただし、try-catch-clauseを単純に省略することはできません。


1
このような場合throw new Error(e)、私は何も見逃さない(または実際に壊れたプラットフォームを報告する)ことを絶対に確実にするためだけに追加したくなるでしょう。
ハレ・ナスト

@HalleKnastはい。これについては、softwareengineering.stackexchange.com
questions / 122233 /…

5

原則として、いいえ。スタックに例外をスローするか、何が起こったのかを記録する必要があります。

ただし、文字列を解析して数値に変換するときに、これをよく行います(特に、一度だけ使用してさまざまなものを捨てるマッピングプロジェクトの場合)。

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;

3

場合によっては、例外はまったく例外ではありません。実際、期待されています。この例を考えてみましょう:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

java.lang.Process()にはisAlive()メソッドがないため、まだ生きているかどうかを確認する唯一の方法は、exitValue()を呼び出すことです。これは、プロセスがまだ実行中の場合、IllegalThreadStateExceptionをスローします。


2

私の謙虚な経験では、めったにこの良いことはありません。ただし、走行距離は異なる場合があります。

コードベースでこれを見るたびに、それは食べられた例外が隠されているバグを追跡したからです。別の言い方をすれば、コードを提供しないことにより、アプリケーションにはエラーを示す方法や別のアクションを実行する方法がありません。

たとえば、APIレイヤーでは、例外を過去に通過させたくない場合があります。そのため、その場合は、最も一般的なものをキャッチしてログに記録する傾向があります。もう一度、少なくともデバッグ/診断セッションにチャンスを与えるために。

通常、空にする必要がある場合は、少なくとも何らかのアサーションを追加するだけなので、デバッグセッション中に少なくとも何かが発生します。つまり、処理できない場合は、完全に省略しがちです。


2

IMOには、空のcatchステートメントを使用できる場所が1つあります。例外が予想されるテストケースの場合:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}

1、私はこれを行うことを憎むが、それはJUnitの中で動作します
SAL

@sal:@Test(expected = Exception.class)はどうですか?
-ysolik

@ysolik、悪い習慣
sal

1
@sal:なぜそれが悪い習慣なのですか?
アダムリア

うーん ユニットテストフレームワークでこれを行う方法があります。例 NUnit。予想どおりに失敗することをテストすることが役立つ場合があります。とはいえ、私は本番コードでは決してそれをしません。
エヴァンプライス

2

空のcatch句を持つことが正当化される特定のケースがあると思います-これらは、特定の操作が失敗した場合にコードをそのまま続行させたい場合です。ただし、このパターンは簡単に使いすぎてしまう可能性があると思うので、各使用方法を綿密に調べて、それを処理するより良い方法がないことを確認する必要があります。特に、プログラムが一貫性のない状態のままになっている場合、またはコードの他の部分が後で失敗する可能性がある場合は、これを実行しないでください。


1

例外が発生したときに何らかのレベルの警告が出ないとは思わない-プログラミングの目標の1つはコードを改善することではないでしょうか?例外が10%の時間で発生し、それを知らない場合、例外は常にそれほど悪いか、悪いことになります。

ただし、例外がコードの処理に実際の害を及ぼさない場合、なぜユーザーを例外にさらすのかという反対側を見ます。私が通常実装するソリューションは、ロガークラスによってログに記録することです(つまり、職場で書いたすべてのクラスにあります)。

良性の例外である場合は、Loggerの.Debug()メソッドを呼び出します。それ以外の場合、Logger.Error()(および(おそらく)スロー)。

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

ちなみに、ロガーの実装では、.Debug()メソッドを呼び出すと、App.Configファイルでプログラム実行ログレベルがデバッグモードになっている場合にのみログが記録されます。


(何らかの理由で)_log.Debugが例外をスローした場合はどうなりますか?
JohnL

その後、.Debug()が例外を食べて、私はそれについて決して知りません。しかし、例外を頻繁にスローするコードについて感じるよりも、ロガーについて自信があります。それが心配なら、catchブロックへのエントリを監視し、後で使用するために保存する何らかのパターンを実装できると思います。いずれにせよ-ポイントを取る
ティムクラソン

1

存在チェックは良いユースケースです:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

別のケースは、finally句が使用される場合です。

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

これと同様のユースケースに関連するECMAScript のcatchバインディングの省略を許可する提案があります。

参照資料


別の例:nodejsでは、fs.existsメソッド(trueまたはfalseを返す)は推奨されません(nodejs.org/dist/latest-v10.x/docs/api/…)。ファイルが存在しない場合。ファイルが存在する場合にのみ何かをしたい場合、catch句はまったく不要です。
systemovich

0

このようなことを本当にする必要があったのは、コンソールアプリのログクラスを使用するときだけでした。最上位のグローバルキャッチハンドラーがあり、STDOUTにエラーを出力し、エラーをファイルに記録し、> 0のエラーコードでアプリを閉じました。

ここでの問題は、時々、ファイルへのロギングが失敗することでした。ディレクトリのアクセス許可により、ファイルの書き込みが停止され、ファイルは何でも排他的にロックされました。それが発生した場合、例外の詳細を記録するとロガーが同じアクションを実行するため、他の場所と同じ方法で例外を処理できませんでした(キャッチ、関連する詳細を記録、より良い例外タイプでラップなど)既に失敗したファイルに書き込もうとしています)。彼らが再び失敗したとき...あなたは私がどこへ行くのかを見る。無限ループに入ります。

try {}ブロックの一部をドロップすると、メッセージボックスに表示される例外で終わる可能性があります(サイレント構成アプリに必要なものではありません)。したがって、ログファイルに書き込むメソッドには、空のcatchハンドラがあります。エラーコードが0を超えているため、エラーは埋められません。無限ループが停止するだけで、アプリを正常に終了できます。

もちろん、なぜ空なのかを説明するコメントがあります。

(これを以前に投稿するつもりだったので、私は忘却に火が付くのをただ恐れていました。しかし、私だけではないので...)


0

最後に配置された最も重要なピースが何であれ、何らかのシーケンスを実行する必要がある場合は問題ありません。

例えば。ホストからコントローラーへのモーションコントロール通信。緊急の状況では、移動を中止し、軌道生成を停止し、モーターを減速し、ブレークを有効にし、アンプを無効にするための準備ステップがいくつかあります。その後、バスパワーを停止する必要があります。すべては順番に実行する必要があり、ステップの1つが失敗した場合、ハードウェアを損傷するリスクが認められていても、残りのステップを実行する必要があります。上記のすべてが失敗したとしても、とにかくバスのキルパワーが発生する必要があるとしましょう。


それは、あなたが何もしないという意味ではなく、あなたがやることはフローを中断しないということだけです。例外をキャッチし、メモリに保存し(ディスク書き込み遅延なし)、finallyステートメントでパワーを強制終了します。次に、保存した情報を使用して何をします。
ローレンペクテル

0

システムリソースを正常に閉じてエラーから回復する

例えば。ユーザーにフィードバックを提供しないサービスをバックグラウンドで実行している場合、エラーの表示をユーザーにプッシュしたくない場合があります、使用されているリソースを適切に閉じる必要があります。

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

理想的には、デバッグモードでcatchを使用してエラーをキャッチし、テスト用にコンソールまたはログファイルに出力します。実稼働環境では、一般に、エラーがスローされたときにバックグラウンドサービスがユーザーを中断しないことが期待されるため、エラー出力を抑制する必要があります。

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