例外処理

スポンサーリンク

例外

プログラム実行時に発生する異常を,例外 (exception) と呼びます。
例外処理機構を利用することで,問題の検出と解決を分離して記述できるようになります。

try-catch-finally 文

次のように try-catch-finally 文を使うと,プログラムの実行時エラー (例外) を処理できます。

try
{
    // 例外が発生する可能性のある処理
}
catch (例外型1 変数)
{
    // 例外をキャッチした場合に行う処理
}
...
finally
{
    // 最後に必ず行うべき処理
}

catch 節は catch (例外型) { ... } または catch { ... } と書くこともできます。
catch 節,finally 節はどちらか一方のみ省略できます。

次のプログラムは,try-catch 文を用いたプログラムの例です。

using System;

class Program
{
    static void Main()
    {
        try
        {
            int a = 1, b = 0;
            int c = a / b;
        }
        catch (OverflowException e)
        {
            Console.WriteLine("OverflowException をキャッチ");
            Console.WriteLine(e.Message);
        }
        catch (DivideByZeroException e)
        {
            Console.WriteLine("DivideByZeroException をキャッチ");
            Console.WriteLine(e.Message);
        }
        catch (Exception e)
        {
            Console.WriteLine("Exception をキャッチ");
            Console.WriteLine(e.Message);
        }
    }
}
DivideByZeroException をキャッチ
0 で除算しようとしました。

DivideByZeroException は,DivideByZeroException と Exception の両方にマッチします。
ただし,DivideByZeroException の catch 節が先に書かれているので,この節でキャッチされます。

次のプログラムは,try-finally 文を使ったプログラムの例です。
finally 節で Dispose メソッドを実行することにより,例外が発生したときでも確実にリソースが解放されるようにしています。

StreamReader reader = new StreamReader("hoge.txt");
try
{
    text = reader.ReadToEnd();
}
catch (IOException e)
{
    Console.WriteLine("IOException をキャッチ");
    console.WriteLine(e.Message);
}
finally
{
    if (reader != null)
    {
        reader.Dispose();
    }
}

例外クラス

組込みの代表的な例外クラスには,次のようなものがあります。

例外クラス説明
Exceptionすべての例外クラスの基底
DivideByZeroExceptionゼロ除算を実行した
IndexOutOfRangeException範囲外の添字を指定した
OverflowException桁あふれが発生した
NullReferenceException空のオブジェクトにアクセスした
InvalidCastException無効なキャストを実行した
InvalidOperationException無効な操作を実行した

Exception クラス (System 名前空間) は,すべての例外クラスの基底クラスです。

System 名前空間
Exception クラス [MSDN]

プロパティ

項目説明
string Messageメッセージ
string StackTraceスタックトレース

例外のスロー

次のプログラムは,例外オブジェクトを生成してスローする例です。

using System;

class Program
{
    // x の y 乗を返す
    static int Power(int x, int y)
    {
        if (y < 0)
        {
            throw new ArgumentOutOfRangeException(
                "y", "0 以上の整数を指定する必要があります。");  // 例外をスロー
        }

        int ret = 1;
        for (int i = 0; i < y; i++)
        {
            ret *= x;
        }
        return ret;
    }

    static void Main()
    {
        try
        {
            int n = Power(2, -1);
            Console.WriteLine(n);
        }
        catch (ArgumentOutOfRangeException e)
        {
            Console.WriteLine("キャッチした例外: " + e.Message);
        }
    }
}
キャッチした例外: 0 以上の整数を指定する必要があります。
パラメーター名: y

例外オブジェクトを生成するには,普通のクラスからオブジェクトを生成するのと同様に new 演算子を使用します。
ここでは,ArgumentOutOfRangeException 例外にメッセージ "引数 y は 0 以上である必要があります" を設定して,スローしています。

例外の再スロー

catch 節でキャッチした例外は,その catch 節で再びスローすることもできます。
こうすることで,例外の情報をメソッドの呼び出し元に伝播させることができます。

using System;

class Program
{
    static void Method2()
    {
        throw new Exception();  // 例外をスロー
    }

    static void Method1()
    {
        try
        {
            Method2();
        }
        catch (Exception e)
        {
            throw;  // キャッチした例外を再スロー
        }
    }

    static void Main()
    {
        Method1();
    }
}
ハンドルされていない例外: System.Exception: 種類 'System.Exception' の例外がスローされました。
   場所 Program.Method2() 場所 C:\projects\Test1\Test1\Program.cs:行 11
   場所 Program.Method1() 場所 C:\projects\Test1\Test1\Program.cs:行 22
   場所 Program.Main() 場所 C:\projects\Test1\Test1\Program.cs:行 28

Method2 では自分で例外オブジェクトを作成してスローしています。
Method1 では Method2 から来た例外をキャッチし,再スローしています。

例外を再スローするとき,スタックトレースを保持するためには,throw e; ではなく throw; と書く必要があります。
もし throw e; と書いてしまうと,スタックトレースがリセットされ,例外の発生原因の特定が難しくなってしまう場合があるため,注意が必要です。

実際,Method1 を次のように変えて throw e; で例外を再スローすると,スタックトレースがリセットされてしまうことがわかります。

    static void Method1()
    {
        try
        {
            Method2();
        }
        catch (Exception e)
        {
            throw e;  // キャッチした例外を再スロー
        }
    }
ハンドルされていない例外: System.Exception: 種類 'System.Exception' の例外がスローされました。
   場所 Program.Method1() 場所 C:\projects\Test1\Test1\Program.cs:行 22
   場所 Program.Main() 場所 C:\projects\Test1\Test1\Program.cs:行 28
スポンサーリンク