次の方法で共有


例外の使用 (C# プログラミング ガイド)

更新 : 2007 年 11 月

C# では、プログラムの実行時に発生したエラーは、例外という機能を通じてプログラムに伝えられます。例外は、エラーが発生したコードによってスローされ、エラーを修正できるコードによってキャッチされます。例外は、.NET Framework 共通言語ランタイム (CLR: Common Language Runtime) またはプログラムのコードによってスローできます。スローされた例外は、例外の catch ステートメントが見つかるまで呼び出し履歴をさかのぼります。キャッチされない例外は、システムが提供する汎用例外ハンドラによって処理され、ダイアログ ボックスが表示されます。

例外は、Exception から派生したクラスによって表されます。このクラスは、例外の型を識別し、その例外に関する詳細情報を含むプロパティを保持します。例外をスローするには、例外の派生クラスのインスタンスを作成し、必要に応じて例外のプロパティを設定してから、throw キーワードを使用してオブジェクトをスローします。次に例を示します。

class CustomException : Exception
{
    public CustomException(string message)
    {

    }

}
private static void TestThrow()
{
    CustomException ex =
        new CustomException("Custom exception in TestThrow()");

    throw ex;
}

例外がスローされると、ランタイムは現在のステートメントが try ブロック内に存在するかどうかを確認します。存在する場合は、try ブロックに関連付けられている catch ブロックが例外をキャッチできるかどうかを確認します。通常は、この Catch ブロックによって例外の型が指定されます。catch ブロックの型と、例外または例外の基本クラスの型が一致する場合、catch ブロックはメソッドを処理できます。次に例を示します。

static void TestCatch()
{
    try
    {
        TestThrow();
    }
    catch (CustomException ex)
    {
        System.Console.WriteLine(ex.ToString());
    }
}

例外をスローするステートメントが try ブロックに存在しない場合、またはステートメントを含む try ブロックに適合する catch ブロックが存在しない場合、ランタイムは、呼び出し側のメソッドで try ステートメントと catch ブロックを探します。ランタイムは、呼び出し履歴を続けて、対応する catch ブロックを検索します。catch ブロックが見つかり、実行されると、その catch ブロックの後にある次のステートメントに制御が渡されます。

try ステートメントには、複数の catch ブロックを含めることができます。例外を処理できる最初の catch ステートメントが実行され、その後の catch ステートメントは、対応していても無視されます。したがって、catch ブロックは、特定性の高いもの (または最派生のもの) から低いものに順番に配置する必要があります。たとえば、次のような方法があります。

static void TestCatch2()
{
    System.IO.StreamWriter sw = null;
    try
    {
        sw = new System.IO.StreamWriter(@"C:\test\test.txt");
        sw.WriteLine("Hello");
    }

    catch (System.IO.FileNotFoundException ex)
    {
        System.Console.WriteLine(ex.ToString());  // put the more specific exception first
    }

    catch (System.IO.IOException ex)
    {
        System.Console.WriteLine(ex.ToString());  // put the less specific exceptions last
    }
    finally 
    {
        sw.Close();
    }

    System.Console.WriteLine("Done");  // this statement is executed after the catch block
}

catch ブロックが実行される前に、ランタイムにより finally ブロックがチェックされます。Finally ブロックにより、中止された try ブロックによって残されることがあるあいまいな状態をクリーンアップすること、またはランタイムのガベージ コレクタがオブジェクトを終了させるのを待たずに外部リソース (グラフィック ハンドル、データベース接続、ファイル ストリームなど) を解放することができます。次に例を示します。

static void TestFinally()
{
    System.IO.FileStream file = null;
    //Change the path to something that works on your machine
    System.IO.FileInfo fileInfo = new System.IO.FileInfo(@"C:\file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        if (file != null)
        {
            file.Close();
        }
    }

    try
    {
        file = fileInfo.OpenWrite();
        System.Console.WriteLine("OpenWrite() succeeded");
    }
    catch (System.IO.IOException)
    {
        System.Console.WriteLine("OpenWrite() failed");
    }
}

WriteByte() が例外をスローした場合は、file.Close() を呼び出さないと、ファイルを再度開こうとする 2 番目の try ブロックのコードは失敗し、ファイルはロックされたままになります。finally ブロックは、例外がスローされても実行されるので、上の例の finally ブロックにより、ファイルを適切に閉じて、エラーを回避できます。

例外がスローされた後に、対応する catch ブロックが呼び出し履歴に見つからない場合は、次のいずれかが生じます。

  • 例外がデストラクタの内部で発生した場合、デストラクタは中止され、基本デストラクタ (存在する場合) が呼び出されます。

  • 呼び出し履歴に静的コンストラクタまたは静的フィールド初期化子が含まれている場合、TypeInitializationException がスローされ、この新しい例外の InnerException プロパティに元の例外が割り当てられます。

  • スレッドの開始位置に到達すると、スレッドは終了します。

参照

概念

C# プログラミング ガイド

参照

例外と例外処理 (C# プログラミング ガイド)