C# では、実行時のプログラムのエラーは、例外と呼ばれるメカニズムを使用してプログラムを介して伝達されます。 例外は、エラーが発生したコードによってスローされ、エラーを修正できるコードによってキャッチされます。 例外をスローできるのは、.NET ランタイム、またはプログラム内のコードです。 スローされた例外は、例外の catch ステートメントが見つかるまで呼び出し履歴をさかのぼります。 キャッチされない例外は、ダイアログ ボックスを表示するシステムによって提供される汎用例外ハンドラーによって処理されます。
例外は、 Exceptionから派生したクラスによって表されます。 このクラスは、例外の種類を識別し、例外に関する詳細を持つプロパティを含みます。 例外をスローするには、例外派生クラスのインスタンスを作成し、必要に応じて例外のプロパティを構成してから、 throw キーワードを使用してオブジェクトをスローします。 例えば次が挙げられます。
class CustomException : Exception
{
public CustomException(string message)
{
}
}
private static void TestThrow()
{
throw new CustomException("Custom exception in TestThrow()");
}
例外がスローされると、ランタイムは現在のステートメントをチェックして、 try ブロック内にあるかどうかを確認します。 存在する場合は、catch ブロックに関連付けられている try ブロックをチェックして、例外をキャッチできるかどうかを確認します。
Catch ブロックは通常、例外の種類を指定します。 catch ブロックの型が例外と同じ型、または例外の基底クラスである場合、 catch ブロックはメソッドを処理できます。 例えば次が挙げられます。
try
{
TestThrow();
}
catch (CustomException ex)
{
System.Console.WriteLine(ex.ToString());
}
例外をスローするステートメントが try ブロック内にない場合、またはそれを囲む try ブロックに一致する catch ブロックがない場合、ランタイムは呼び出し元のメソッドで try ステートメントと catch ブロックをチェックします。 ランタイムは呼び出し元スタックを続行し、互換性のある catch ブロックを検索します。
catch ブロックが見つかり、実行されると、そのcatch ブロックの後の次のステートメントに制御が渡されます。
try ステートメントには、複数のcatch ブロックを含めることができます。 例外を処理できる最初の catch ステートメントが実行されます。互換性がある場合でも、次の catch ステートメントは無視されます。 catch ブロックを、最も具体的なもの (または最も派生したもの) から最も具体的でないものの順に並べます。 例えば次が挙げられます。
using System;
using System.IO;
namespace Exceptions
{
public class CatchOrder
{
public static void Main()
{
try
{
using (var sw = new StreamWriter("./test.txt"))
{
sw.WriteLine("Hello");
}
}
// Put the more specific exceptions first.
catch (DirectoryNotFoundException ex)
{
Console.WriteLine(ex);
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex);
}
// Put the least specific exception last.
catch (IOException ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Done");
}
}
}
catch ブロックが実行される前に、ランタイムはfinally ブロックをチェックします。
Finally ブロックを使用すると、プログラマは、中止された try ブロックから残される可能性のあるあいまいな状態をクリーンアップしたり、ランタイム内のガベージ コレクターがオブジェクトを終了するのを待たずに外部リソース (グラフィックス ハンドル、データベース接続、ファイル ストリームなど) を解放したりできます。 例えば次が挙げられます。
static void TestFinally()
{
FileStream? file = null;
//Change the path to something that works on your machine.
FileInfo fileInfo = new System.IO.FileInfo("./file.txt");
try
{
file = fileInfo.OpenWrite();
file.WriteByte(0xF);
}
finally
{
// Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
file?.Close();
}
try
{
file = fileInfo.OpenWrite();
Console.WriteLine("OpenWrite() succeeded");
}
catch (IOException)
{
Console.WriteLine("OpenWrite() failed");
}
}
WriteByte() が例外をスローした場合、try を呼び出さないと、2 番目のfile.Close() ブロックでファイルを再度開こうとするコードが失敗し、ファイルはロックされたままになります。
finally ブロックは例外がスローされた場合でも実行されるため、前の例のfinally ブロックを使用すると、ファイルを正しく閉じ、エラーを回避できます。
例外がスローされた後に呼び出し履歴に互換性のある catch ブロックが見つからない場合は、次の 3 つのいずれかが発生します。
- 例外が ファイナライザー内にある場合、ファイナライザーは中止され、ベース ファイナライザーがある場合は呼び出されます。
- 呼び出し履歴に静的コンストラクターまたは静的フィールド初期化子が含まれている場合は、 TypeInitializationException がスローされ、元の例外が新しい例外の InnerException プロパティに割り当てられます。
- スレッドの開始に達すると、スレッドは終了します。
.NET