try { // } catch (Exception e) { // } finally { // }try-catch-finally (C# リファレンス) | MSDN
継承関係にある複数の型で捕捉するときは、派生型を先に記述する必要があります。この問題は「前の catch 句はこれ、またはスーパー型 ('***') の例外のすべてを既にキャッチしました。」としてCS0160のエラーで検出できます。
try { throw new Exception("B"); } catch (Exception e) when (e.Message == "A") // 条件が一致しないため、捕捉されない { } catch (Exception e) when (e.Message == "B") // 捕捉される { }コンテキスト キーワード when - C# リファレンス | Microsoft Learn
catch (Exception e) when (e is MyException1 || e is MyException2) { }
catch (Exception e) { if (e is MyException1 || e is MyException2) { } else { throw; } }
Action action = () => { }; catch (MyException1) { action(); } catch (MyException2) { action(); }
引数を省略すると、すべての型の例外が捕捉されます。
catch { }
これはC++で、catch(...) {}
とすることと同じです。C# 2.0以降ではCLS非準拠の例外はRuntimeWrappedExceptionに変換されるため、
catch (Exception e) { }
としても、すべての例外を捕捉できます。
最上位の例外ハンドラであるか、捕捉後に再スローするのでなければ、このようにすべての型の例外を捕捉すべきではありません。Exception and SystemException - Using Standard Exception Types | MSDN
.NET Framework 4.0以降、CLRで予約されているメモリの外部で発生した例外は、SEH (structured exception handling / 構造化例外処理) 内のcatchでは捕捉されません。これを捕捉できるようにするには、例外が投げられるメソッドにHandleProcessCorruptedStateExceptionsAttribute属性を適用するか、構成ファイルに<legacyCorruptedStateExceptionsPolicy>を追加します。 AccessViolationException and try/catch blocks - AccessViolationException Class (System) | Microsoft Learn c# - How to handle AccessViolationException - Stack Overflow
ただしその例外に対処できないならば、捕捉しないようにします。
finally句はtry句で例外が発生しても実行されるため、try句で割り当てたリソースの解放などに使用できます。
try { throw new Exception(); // ここから catchへ移動する } catch (Exception) { // 例外に対する処理 // ここから finallyへ移動する } finally { // finally句の、次の処理へ移動する } // ここも処理される // 例外は捕捉されたため、呼び出し元へ伝播しない
例外に関与しないならば、catch句は省略できます。しかしこのときリソースの解放が目的ならば、try-finallyを用いずusingステートメントで記述すべきです。
try { throw new Exception(); // finallyへ移動する } finally { // 例外は捕捉されていないため、呼び出し元へ伝播する } // ここには到達しない
通常finally句は、制御がtry句を離れるときに実行されます。それにはbreak、continue、goto、returnも含まれ、例外とは無関係に実行されます。
try { return; // finallyへ移動する } finally { // このブロックを抜けると、メソッドを抜ける } // ここには到達しない
finally句で例外を投げると、そのtry句から投げられた例外は捕捉できなくなります。
try { try { throw new MyException1(); } finally { throw new MyException2(); } } catch (MyException1) { } // これは捕捉されない catch (MyException2) { } // これは捕捉される
例外が捕捉されたときにはfinally句は確実に実行されますが、捕捉されずアプリケーションが終了するときには、コンピューターの設定に依存します。 try-finally (C# リファレンス) | Microsoft Learn Finally文が実行されないケースはあるか? - .NET Tips (VB.NET,C#...)
例外を捕捉しないと、アプリケーションは終了させられます。
コンソールに次のように出力され、
ハンドルされていない例外: System.Exception: MESSAGE 場所 sample.Program.Main(String[] args) 場所 C:\Program.cs:行 10
ダイアログボックスで、「AssemblyTitle は動作を停止しました」「問題が発生したため、プログラムが正しく動作しなくなりました。プログラムは閉じられ、解決策がある場合は Windows から通知されます。」のように通知されます。
ダイアログボックスで、「アプリケーションのコンポーネントで、ハンドルされていない例外が発生しました。[続行] をクリックすると、アプリケーションはこのエラーを無視し、続行しようとします。[終了] をクリックすると、アプリケーションは直ちに終了します。」のように通知され、例外のメッセージもそこに表示されます。
メインスレッド以外から例外が投げられた場合はこのような詳細はなく、ただ「AssemblyTitle は動作を停止しました」と表示されるのみです。この場合にはイベント ビューアーでソースが[Application Error]のイベントを探すことで、例外の詳細を確認できます。
static void Main() { // メインスレッドで、発生した例外 Application.ThreadException += (object sender, System.Threading.ThreadExceptionEventArgs e) => { Console.Write(e.Exception.Message); }; // メインスレッド以外で、発生した例外 AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => { Exception exception = e.ExceptionObject as Exception; Console.Write(exception.Message); }; Application.Run(new Form1()); }.NET TIPS:適切に処理されなかった例外をキャッチするには? - @IT 一色政彦 (2005/07/01)
catch句があるにもかかわらず「型 '***' の例外が *** で発生しましたが、マネージとネイティブの境界の前でハンドルされませんでした」として捕捉できないときには、デバッグの設定で[例外が AppDomain またはマネージ/ネイティブの境界を越える場合にブレークする (マネージのみ)]のチェックを外します。
例外はスタックの上位へ伝わるため、その経路にない位置では捕捉できません。
Action action = null; try { // ここではデリゲートがインスタンス化されるだけで呼び出されず、例外は投げられない action = () => { throw new Exception(); }; } catch (Exception) { // ここでは捕捉されない } try { action(); // ここでデリゲートが呼び出され、そこから例外が投げられる } catch (Exception) { // ここで捕捉される }
ExceptionやSystemExceptionをスローしないようにし、適当なクラスが存在しないならば新たに定義してスローします。Exception and SystemException - Using Standard Exception Types | MSDN
Exceptionまたはその派生クラスを継承してクラスを作成します。
ApplicationExceptionを継承しないようにします。Remarks - ApplicationException Class (System) | Microsoft Learn
Exceptionを継承したならば、このクラスがISerializableインターフェイスを実装するためSerializableAttribute属性を指定します。これを指定しないと、コード分析により「ISerializable 型を SerializableAttribute に設定します」としてCA2237で警告されます。またpublicとしないと外部のコードで捕捉されたときに十分な情報を得られないため、「例外は public として設定する必要があります」としてCA1064で警告されます。
[Serializable] public class MyException : Exception, ISerializable { public MyException() : base() { } public MyException(string message) : base(message) { } public MyException(string message, Exception inner) : base(message, inner) { } // 逆シリアル化に用いられる protected MyException(SerializationInfo info, StreamingContext context) : base(info, context) { } // カスタムのシリアル化を処理する [SecurityCritical] public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); } }カスタム例外を実装します。 - Exception Class (System) | Microsoft Learn
捕捉した例外をそのまま再度スローする場合は、throw;
とだけ記述します。
catch (Exception e) { throw; }
throw;とするとException.StackTraceが維持されますが、throw e;とするとこれが更新されます。The throw statement - Exception-handling statements - throw and try, catch, finally - C# | Microsoft Learn
すべてのExceptionの基本クラスです。
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)] [System.Runtime.InteropServices.ComVisible(true)] [System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDual)] [System.Serializable] public class Exception : System.Runtime.InteropServices._Exception, System.Runtime.Serialization.ISerializableException Class (System) | Microsoft Learn
型 | プロパティ | 内容 |
---|---|---|
IDictionary | Data | 例外に関する追加のユーザー定義情報を提供する、キー/値ペアのコレクション |
string | HelpLink | この例外に関連付けられているヘルプ ファイルへのリンク |
int | HResult | 特定の例外に割り当てられているコード化数値であるHRESULT
.NET Framework 4.5以降のgetterはpublic。それより前はgetterとsetter共にprotected |
Exception | InnerException | 現在の例外の原因となるSystem.Exceptionインスタンス |
string | Message | 現在の例外を説明するメッセージ |
string | Source | エラーの原因となったアプリケーションまたはオブジェクトの名前 |
string | StackTrace | 呼び出し履歴で直前のフレームの文字列形式 |
MethodBase | TargetSite | 現在の例外がスローされたメソッド |
任意の情報を追加できます。
Exception e = new Exception(); e.Data.Add("info1", 1); e.Data.Add("info2", "A"); throw e;
システム例外の基本クラスです。
[System.Runtime.InteropServices.ComVisible(true)] [System.Serializable] public class SystemException : ExceptionSystemException Class (System) | Microsoft Learn
[System.Serializable] public class AggregateException : ExceptionAggregateException クラス (System) | Microsoft Learn
AggregateExceptionのインスタンスから得られるInnerExceptionは、InnerExceptions[0]と同一です。
AggregateException e = new AggregateException(new[] { new Exception("0"), new Exception("1"), new Exception("2"), }); string s = e.InnerException.Message; // "0" string s0 = e.InnerExceptions[0].Message; // "0" string s1 = e.InnerExceptions[1].Message; // "1" bool r = object.ReferenceEquals(e.InnerException, e.InnerExceptions[0]); // true