C#のtry~catch~finallyの使い方

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) { }
}