C#における例外の再スローを正しく行う方法についてまとめました。
Unity2019.3.3
.Net Standard 2.0
※C#の話題ですがUnityで実行確認しています
catch句で再スローする方法
まずcatch句で補足した例外を再スローすることを考えます。
まず正しい方法は以下の通りです。
using System; public class Example { private static void Test() { try { SomeMethod01(); } catch (Exception e) { throw; } } private static void SomeMethod01() => SomeMethod02(); private static void SomeMethod02() => SomeMethod03(); private static void SomeMethod03() => throw new Exception(); }
上記のコードを実行すると以下のようにスタックトレース付きの例外がスローされます。
Exception: Exception of type 'System.Exception' was thrown. Example.SomeMethod03 () (at Assets/Example.cs:43) Example.SomeMethod02 () (at Assets/Example.cs:42) Example.SomeMethod01 () (at Assets/Example.cs:41) Example.Test () (at Assets/Example.cs:19)
ここで、catch句内を以下のように書いてみます(悪い例です)。
// ※※※悪い例です※※※ catch (Exception e) { throw e; }
もちろん例外はスローされるのですが、以下のようにスタックトレースが破棄された結果となってしまいます。
Exception: Exception of type 'System.Exception' was thrown. Example.Test () (at Assets/Example.cs:18)
このようにcatch句内で再スローする場合にはスタックトレースを破棄しないよう注意が必要です。
catch句外で再スローする場合にはExceptionDispatchInfoを使う
次にcatch句で補足した例外をcatch句の外で再スローするケースを考えます。
まず以下のように保持した例外をそのままスローします(悪い例です)。
// ※※※悪い例です※※※ using System; using UnityEditor; public class Example { private static void Test() { Exception ex = null; try { SomeMethod01(); } catch (Exception e) { ex = e; } ThrowException(ex); } private static void ThrowException(Exception ex) { throw ex; } private static void SomeMethod01() => SomeMethod02(); private static void SomeMethod02() => SomeMethod03(); private static void SomeMethod03() => throw new Exception(); }
例外はスローされますが、以下のようにスタックトレース情報が消えてしまっています。
Exception: Exception of type 'System.Exception' was thrown. Example.ThrowException (System.Exception ex) (at Assets/Example.cs:25) Example.Test () (at Assets/Example.cs:20)
最初に例外がスローされたときのスタックトレースを保持したまま再スローする場合には、
以下のようにExceptionDispatchInfo
を使って再スロー処理を書き替えます。
private static void ThrowException(Exception ex) { ExceptionDispatchInfo.Capture(ex).Throw(); }
するとスタックトレースが保持されたまま例外を再スローできます。
Exception: Exception of type 'System.Exception' was thrown. Example.SomeMethod03 () (at Assets/Example.cs:30) Example.SomeMethod02 () (at Assets/Example.cs:29) Example.SomeMethod01 () (at Assets/Example.cs:28) Example.Test () (at Assets/Example.cs:13) --- End of stack trace from previous location where exception was thrown --- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () (at <437ba245d8404784b9fbab9b439ac908>:0) Example.ThrowException (System.Exception ex) (at Assets/Example.cs:25) Example.Test () (at Assets/Example.cs:20)