1. ホーム
  2. c#

[解決済み】例外処理にtry catchを使うのはベストプラクティスなのか?

2022-04-11 09:23:38

質問

シニアデベロッパーと称する人の同僚のコードをメンテナンスしていると、次のようなコードをよく見かけます。

try
{
  //do something
}
catch
{
  //Do nothing
}

または、以下のようにログファイルにログ情報を書き込むこともあります。 try catch ブロック

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

私は、彼らが行ったことがベストプラクティスであるかどうか疑問に思っています。私の考えでは、ユーザーはシステムで何が起こっているかを知るべきであると思うからです。

どのように解決するのか?

私の例外処理方針は。

  • をキャッチするには すべての未処理例外 にフックすることで Application.ThreadException event で、決定します。

    • UIアプリケーションの場合:お詫びのメッセージを添えてユーザーにポップする(WinForms)
    • サービスまたはコンソールアプリケーションの場合:ファイルにログを記録する(サービスまたはコンソール)

それから、いつも囲むのは 外部で実行されるすべてのコード try/catch :

  • WinForms インフラストラクチャによって発生するすべてのイベント (ロード、クリック、SelectedChanged...)
  • サードパーティコンポーネントが発生させるすべてのイベント

そして、「try/catch」で囲みます。

  • すべての というオペレーションを 常に動作するわけではないことを知っている (IO演算、ゼロ除算の可能性がある計算...)。そのような場合、私は新しい ApplicationException("custom message", innerException) 実際に起こったことを記録するために

さらに、私は次のことを心がけています。 例外を正しくソートする . という例外があります。

  • すぐにユーザーに表示する必要がある

  • は、カスケード問題を避けるために、物事が起こったときにまとめるための余分な処理を必要とします (例: .EndUpdate を finally セクションの間に TreeView フィル)

  • ユーザーは気にしないが、何が起こったかを知ることは重要である。だから、私はいつもログをとっています。

  • イベントログに

  • またはディスク上の.logファイル

を行うのがよいでしょう。 例外を処理するための静的メソッドをいくつか設計する をアプリケーションのトップレベルのエラーハンドラで使用します。

また、無理やりやってみたりもします。

  • 記憶する すべての例外はトップレベルにバブルアップされる . 例外ハンドラをあちこちに配置する必要はありません。
  • 再利用可能な関数や深く呼び出された関数は、例外を表示したりログに残したりする必要がありません。例外は自動的にバブルアップされるか、例外ハンドラのカスタムメッセージとともに再スローされます。

では最後に。

悪いこと

// DON'T DO THIS; ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

役立たず。

// DON'T DO THIS; IT'S USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

catchを付けずにtry finallyを行うことは、全く問題ありません。

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURRED OR NOT
    listView1.EndUpdate();
}

トップレベルでやっていること

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --
    
    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

いくつかの呼ばれる関数でやっていること。

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module:", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

例外処理(Custom Exceptions)でやるべきことはたくさんありますが、私が行うシンプルなアプリケーションでは、これらのルールで十分だと考えています。

ここでは、キャッチした例外を快適に処理するための拡張メソッドの例を紹介します。これらは連鎖的に実装されており、独自のキャッチ例外処理を簡単に追加することができます。

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}