例外の使用 (C# プログラミング ガイド)
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)
{
// Put the more specific exception first.
System.Console.WriteLine(ex.ToString());
}
catch (System.IO.IOException ex)
{
// Put the less specific exception last.
System.Console.WriteLine(ex.ToString());
}
finally
{
sw.Close();
}
System.Console.WriteLine("Done");
}
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 プロパティに元の例外が割り当てられます。
スレッドの開始位置に到達すると、スレッドは終了します。