更新 : 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 プロパティに元の例外が割り当てられます。
スレッドの開始位置に到達すると、スレッドは終了します。