C#のtry ~ catch(+ ~ finally)構文の使い方について紹介したいと思います。
この構文は、(1)通常処理するtry節、(2)例外が起きた時にそれを処理するためのcatch節と、(3)tryを抜ける時に必ず実行されるfinally節で構成されます。
と言っても構文自体は非常に簡単で、以下のように記述するだけです。
try { // (1) 通常の処理を記述 } catch(XXXException e) { // (2) XXXExceptionを受けた時の処理 } catch(YYYException e) { // (2) 上記と別の種類を補足できるブロックが(いくつでも)記述できる } finally { // (3) 最後に必ず実行する処理 }
tryを書いた場合、catchかfinallyのどちらかは必ず書く必要があります。つまり、以下のようにどちらか片方だけしか書かないでもOKです。
// よくある例外を補足して処理する構文 try { } catch (XXXException e) { } // catch節を書かないでfinallyだけ書く事もできる try { if(XXX) return; // 途中でreturn throw new NotImplementedException(); // 途中で例外が発生 } finally { // 途中でExceptionが起きたり、returnしてもfinallyが必ず実行される。 }
例外を再throwする
一回例外をcatch節で受けて何か処理をした後その例外を再度throw出来ます。
catch(XXXException e) { throw; // eを再throwできる。 throw e; // こうするとスタックトレースがリセットされるので非推奨! }
catch節の書き方
catch節も書き方に多少バリエーションがあるので合わせて紹介したいと思います。一般的なのが(1)、処理を共通化できるのが(2)、一部の言語マニアしか知らないのが(3)です。
// (1) 個別に例外を受ける catch (IOException e) { } catch (FileNotFouundException e) { } // (2) 全部Exceptionで受けてcatch節の中で型を判断する catch (Exception e) { if(e is IOException) { } else if (e is FileNotFouundException) { } else { throw; } } // (3) C#6.0から使えるフィルター機能を使用する(マニア向け) catch (Exception e) when (e is IOException || e is FileNotFouundException) { // C#6.0の機能なのでUnityでも利用できるかと。 }
余談ですがcatch節に型名を記載しない以下書き方は現在非推奨となっています。使わないようにしましょう。
catch // 「(Exception e)」を書かない { }
【余談】finallyじゃなくてusingを使おう
割とよくありますが以下のようなコードは、finallyではなくusingを使用しましょう。IDisposableを継承しているクラスのリソース解放をfinallyで行うのはC#では最良ではありません。
// IDisposableを継承しているあるStreamクラス XXXStream stream = null; try { stream = new XXXStream(@"c:\aaa.txt"); string message = stream.ToString(); } finally { if(stream == null) { stream.Close(); } }
上記コードを以下のように書き換えいます。
// tryもfinallyも不要 using(var stream = new XXXStream(@"c:\aaa.txt")) { string message = stream.ToString(); }
もしオブジェクトの使用と解放が離れていてfinallyで解放する必要がある場合以下のように記述します。usingで囲めばnullチェックすら必要ありません。
finally { // 自動でnullチェックして解放くれる using(this.stream) { } }