【C#】try-catch-finallyによる堅牢な例外処理の基本と実装パターン

アプリケーション開発において、予期せぬエラー(例外)への対策は不可欠です。外部ファイルの読み込み失敗、ネットワークの切断、あるいは不正な計算など、実行時に発生する問題に対して適切に処置を行わない場合、アプリケーションは強制終了してしまいます。

C#ではtry-catch-finally構文を使用することで、これらの例外を捕捉し、適切なエラーハンドリングやリソースの解放を行うことができます。ここでは、タスク進捗率の計算処理を題材に、基本的な例外処理の実装方法について解説します。


目次

try-catch-finallyの基本構造

例外処理の基本構成は以下の3つのブロックから成り立ちます。

  1. try: 例外が発生する可能性があるコードを記述します。
  2. catch: 発生した例外を捕捉し、エラーメッセージの表示やリカバリ処理を行います。
  3. finally: 例外の有無にかかわらず、必ず実行したい処理(リソースの解放など)を記述します。

実践的なコード例:進捗率計算時のゼロ除算対策

以下のコードは、プロジェクトのタスク消化数から進捗率を計算する処理です。「全タスク数」が0の場合(プロジェクト開始前など)に発生するゼロ除算エラーを適切にハンドリングする例です。

using System;

namespace ProjectManagement
{
    class Program
    {
        static void Main()
        {
            // シナリオ:
            // プロジェクトの進捗率を計算したい。
            // しかし、データ不備により「全タスク数」が 0 になっている可能性がある。

            int totalTasks = 0;      // 全タスク数(本来は1以上であるべき)
            int completedTasks = 5;  // 完了タスク数

            try
            {
                // ここで例外が発生する可能性があります。
                // 整数同士の割り算で分母が0の場合、DivideByZeroException がスローされます。
                Console.WriteLine("計算を開始します...");
                
                int progressRate = (completedTasks * 100) / totalTasks;
                
                Console.WriteLine($"進捗率: {progressRate}%");
            }
            catch (DivideByZeroException ex)
            {
                // ゼロ除算が発生した場合の特定の処理
                Console.Error.WriteLine("エラー: タスク数が0のため進捗率を計算できません。");
                // ログ出力などの処理をここに記述します
                // Console.WriteLine(ex.Message); 
            }
            catch (Exception ex)
            {
                // その他の予期せぬ例外すべてを捕捉する汎用的な処理
                Console.Error.WriteLine($"予期せぬエラーが発生しました: {ex.Message}");
            }
            finally
            {
                // 例外の有無にかかわらず必ず実行されるブロック
                // データベース接続の切断やファイルハンドルの解放などをここで行います。
                Console.WriteLine("計算処理を終了します。");
            }
        }
    }
}

実行結果

計算を開始します...
エラー: タスク数が0のため進捗率を計算できません。
計算処理を終了します。

技術的なポイントと注意点

1. 捕捉する例外の順序

catchブロックは上から順に評価されます。そのため、より具体的(派生クラス)な例外を先に記述し、汎用的なExceptionクラスは最後に記述する必要があります。もしExceptionを先に書いてしまうと、すべての例外がそこで捕捉されてしまい、特定の例外(DivideByZeroExceptionなど)に対する個別の処理が実行されなくなります。

2. 整数と浮動小数点の挙動の違い

C#において、ゼロ除算で例外が発生するのは整数型(int, longなど)の場合のみです。 浮動小数点型(double, float)でゼロ除算を行った場合、例外は発生せず、結果はInfinity(無限大)またはNaN(非数)となります。

// double型の場合
double total = 0.0;
double current = 10.0;
double result = current / total; // 例外は発生せず、Infinity になる

3. 例外を握りつぶさない

catchブロック内を空にすること(通称:例外の握りつぶし)は避けるべきです。エラーが発生した事実が隠蔽され、バグの原因究明が極めて困難になります。最低限、ログへの出力を行うか、処理の継続が不可能な場合は例外を再スロー(throw;)する設計が求められます。

まとめ

try-catch-finallyを適切に使用することで、予期せぬエラーが発生してもアプリケーションをクラッシュさせることなく、ユーザーに適切なフィードバックを返すことができます。「正常系」のコードだけでなく、「異常系」の考慮を行うことは、プロフェッショナルな品質を担保する上で必須の要件です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次