今回は、C#の例外処理(try-catch)の使い方についてご紹介します。
例外処理(try-catch)とは
プログラムの実行中に発生する予期しないエラーや異常を例外と呼びます。
C#では、try-catch構文を利用してエラー発生時の処理を分離し、プログラム全体の強制終了を防ぎながら問題を特定・対処できます。
例えばファイル読み込みやネットワーク通信など、外部要因で異常が発生しやすい処理では例外が投げられる可能性が高いです。
こうしたケースに備えて、tryブロック内でエラーを感知し、catchブロックで適切なエラーハンドリングを行うことが重要になります。
try-catch構文の基本
基本的な構文は、以下のように記述します。
1 2 3 4 5 6 7 8 |
try { // 例外が発生する可能性のある処理 } catch (Exception ex) { // 例外発生時の処理(例:ログ出力やメッセージ表示) } |
tryブロック内で何らかの問題が起こるとcatchブロックが呼び出され、プログラムの強制終了を回避します。
例外の種類によって細かくcatchを分けることも可能です。たとえばDivideByZeroExceptionなど、特定のエラーに対して固有の処理を行うことで原因を特定しやすくなります。
また、finallyブロックを追加することで、例外の有無に関わらず必ず実行したいコード(ファイルや接続のクローズ処理など)をまとめられます。
実用的な具体例
例1: ゼロ除算を安全に扱う
以下のコードでは、0除算によるDivideByZeroExceptionをcatchし、エラーが発生した場合はメッセージを表示しつつ0を返す実装例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
using System; public class Calculator { public int SafeDivide(int a, int b) { try { return a / b; } catch (DivideByZeroException ex) { Console.WriteLine("例外発生: " + ex.Message); // エラーが起こった場合は 0 を返す return 0; } } } class Program { static void Main() { Calculator calc = new Calculator(); int result = calc.SafeDivide(10, 0); Console.WriteLine("計算結果 = " + result); } } |
例外発生: Attempted to divide by zero.
計算結果 = 0
例2: ファイル読み込みとfinallyブロック
ファイルやネットワークなどのアンマネージドリソースは、例外発生時でも必ずクローズするように設計する必要があります。
下記の例ではfinallyブロックを使用して、成功・失敗に関わらずストリームをクローズしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
using System; using System.IO; using System.Text; public class FileHandler { public void DisplayFileContent(string path) { StreamReader reader = null; try { reader = new StreamReader(path, Encoding.UTF8); string content = reader.ReadToEnd(); Console.WriteLine(content); } catch (IOException ex) { Console.WriteLine("ファイル読み込み中にエラーが発生: " + ex.Message); } finally { if (reader != null) { reader.Close(); Console.WriteLine("ファイルを閉じました"); } } } } class Program { static void Main() { FileHandler handler = new FileHandler(); handler.DisplayFileContent("example.txt"); } } |
(ファイルが存在し正常に読み込めた場合)
ファイルの内容(例: Hello World!)
ファイルを閉じました
(ファイルが存在しない場合)
ファイル読み込み中にエラーが発生: Could not find file ‘example.txt’.
ファイルを閉じました
例3: throwを使った明示的な例外の発生と再スロー
メソッド内で想定外の状況になった場合に、throwで例外を発生させることができます。
また、catchブロック内でログ出力などを行った後、throw;のみを記述して再スローすれば、元のスタックトレースが保持されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public int Divide(int numerator, int denominator) { if (denominator == 0) { throw new DivideByZeroException("分母が 0 です。"); } return numerator / denominator; } try { int result = Divide(10, 0); } catch (DivideByZeroException ex) { // ログ出力などの対応を行う Console.WriteLine("エラー発生: " + ex.Message); // ここで再スローして上位層に通知 throw; } |
エラー発生: 分母が 0 です。
(再スローされ、呼び出し元がさらに処理を行う可能性があります)
注意点
try-catchを使いこなすためには、以下のポイントに注意する必要があります。
- 不要なキャッチの乱用を避ける
すべての例外を一括でcatchすると、致命的なエラーも握り潰してしまいます。必要に応じて再スローし、上位に知らせるべき例外は適切に処理しましょう。
- 通常の処理フローとの区別
予測可能な状況であれば、if文やTryParseなどを使い、例外はあくまで想定外のエラーに対して用いることを推奨します。
- リソースの解放
ファイルやネットワークなどのリソースは、finallyブロックやusingステートメントで必ず解放を行いましょう。
- 再スロー時の書き方
スタックトレースを保持したい場合はthrow;のみを記述します。throw ex;と書くと情報が失われるため注意が必要です。
- 例外型は具体的に
キャッチする例外は可能な限り具体的な型を指定すると、原因を特定しやすくなります。
まとめ
C#の例外処理は、エラー発生時でもプログラムを安全に動作させるための重要な仕組みです。
try-catch-finallyを適切に使いこなし、例外と通常の制御フローを切り分けることで、可読性と信頼性の高いコードを実現できます。
また、適切な例外型の指定やリソース解放の徹底により、予期しないトラブルを最小限に抑えることが可能です。