はじめに
システム開発において、プログラムが予期せぬ処理の状態に陥った際も異常な動作をしないよう、例外処理を必ず組み込むと思います。また、発生した例外の情報をログ等に出力し、例外発生の原因を特定するために活用することも少なくありません。
今回は例外処理時のハンドリングの違いによる挙動の差異を確認したいと思います。
- 例外処理 learn.microsoft.com
開発環境
- Visual Studio 2022
- C#
- コンソールアプリケーション(.NET 6.0)
確認概要
try-catch
ステートメントを用いてthrow ex
とthrow
の挙動の違いについて比較を行います。
サンプルコード
下記のサンプルコードでは、コンソールに入力された文字に応じて例外を発生させ、catch
句で例外の処理を分岐させます。
ファイル名 | 概要 |
---|---|
Program.cs | メイン処理 |
Util.cs | メイン処理で使用するメソッド |
Program.cs
using SampleApp; public class Program { /// <summary> /// Catch句での処理分岐用 /// </summary> public static bool _IsCatchThrowEx { get; set; } /// <summary> /// メイン処理 /// </summary> static void Main() { try { // 例外集約用のイベントハンドラを追加 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException); // 入力内容を取得 string input = Util.GetInput(); // 入力値に応じて例外を発生させる _IsCatchThrowEx = input == "1" ? true : false; Util.GenerateEx(input); // 結果を出力 Util.Output(input); } catch (Exception ex) { // 入力値が1の場合 if(_IsCatchThrowEx) { throw ex; } // 入力値が1以外の場合 throw; } } /// <summary> /// 発生した例外情報を表示 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public static void CurrentDomain_UnhandledException( object sender, UnhandledExceptionEventArgs e) { Exception? ex = e.ExceptionObject as Exception; if (ex != null) { string msg = "例外情報" + Environment.NewLine; msg += "Message:" + Environment.NewLine; msg += ex.Message + Environment.NewLine; msg += "StackTrace:" + Environment.NewLine; msg += ex.StackTrace; Console.WriteLine(msg); } } }
Util.cs
namespace SampleApp { public static class Util { /// <summary> /// 入力内容を取得 /// </summary> /// <returns></returns> public static string GetInput() { string msg = "1~3のいずれかの値を入力し、Enterを押下してください。"; Console.WriteLine(msg); string? input = Console.ReadLine(); input = string.IsNullOrEmpty(input) ? string.Empty : input; return input; } /// <summary> /// 入力値に応じて例外を発生させる /// </summary> /// <param name="input"></param> public static void GenerateEx(string input) { switch (input) { case "1": case "2": // IndexOutOfRangeExceptionを発生させる string a = string.Empty.Split(input)[1]; break; default: // 何もしない break; } } /// <summary> /// 結果を出力 /// </summary> /// <param name="input"></param> public static void Output(string input) { Console.WriteLine("処理を終了します。"); } } }
差異の確認
例外の処理を Throw ex
と Throw
で行った場合のException
のMessage
とStackTrace
プロパティを比較します。
Throw ex の場合
Message:
Index was outside the bounds of the array.
StackTrace:
at Program.Main() in C:\work\SampleApp\SampleApp\Program.cs:line 36
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Program.Main() in C:\work\SampleApp\SampleApp\Program.cs:line 36
Throw の場合
Message:
Index was outside the bounds of the array.
StackTrace:
at SampleApp.Util.GenerateEx(String input) in C:\work\SampleApp\SampleApp\Util.cs:line 31
at Program.Main() in C:\work\SampleApp\SampleApp\Program.cs:line 26
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
at SampleApp.Util.GenerateEx(String input) in C:\work\SampleApp\SampleApp\Util.cs:line 31
at Program.Main() in C:\work\SampleApp\SampleApp\Program.cs:line 26
出力内容に差が生じています(赤文字部分)。Throw ex
の場合は Program.cs のMain
メソッドの 36行目が発生元となっていますが、Throw
の場合は 26行目になっています。
また、Throw
の場合はUtil.cs の 31行目で例外が発生した情報が出力されていますが、Throw ex
では Util.cs に関する記述がありません。
このように例外の再Throw
の違いにより、StackTrace
に出力される内容が大きく異なります。
Throw ex
を使用した場合、StackTrace
の情報が変更され、意図しない例外がどこで発生したかを特定することが困難になります。
また、障害発生時は迅速な原因の特定および対応が求められる場合が多いため、特別な運用がない限りはThrow
を使用するほうがよいでしょう。
おわりに
運用・保守等ではログの重要性をとても感じます。多くの場合は開発期間よりも運用・保守の期間のほうが長いため、それを意識したシステム開発を行っていけたらと思います。
この記事が参考になれば幸いです。