例外の使用
C# では、例外と呼ばれるメカニズムを使用して、プログラムの実行時に発生したエラーがプログラムに伝えられます。 例外は、エラーが発生したコードによってスローされ、エラーを修正できるコードによってキャッチされます。 例外をスローできるのは、.NET ランタイム、またはプログラム内のコードです。 スローされた例外は、例外の catch
ステートメントが見つかるまで呼び出し履歴をさかのぼります。 キャッチされない例外は、システムが提供する汎用の例外ハンドラーによって処理されます。このとき、ダイアログ ボックスが表示されます。
例外は、Exception から派生したクラスによって表されます。 このクラスは例外の型を識別し、例外に関する詳細情報が含まれたプロパティを保持します。 例外をスローするには、例外の派生クラスのインスタンスを作成し、必要に応じて例外のプロパティを設定してから、throw
キーワードを使用してオブジェクトをスローする必要があります。 次に例を示します。
class CustomException : Exception
{
public CustomException(string message)
{
}
}
private static void TestThrow()
{
throw new CustomException("Custom exception in TestThrow()");
}
例外がスローされると、ランタイムは、現在のステートメントが try
ブロック内に存在するかどうかを確認します。 存在する場合は、try
ブロックに関連付けられている catch
ブロックをチェックして、例外をキャッチできるかどうかを確認します。 通常は、この 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()
から例外がスローされた場合、file.Close()
が呼び出されなければ、ファイルを再度開こうとする 2 番目の try
ブロックのコードが失敗し、ファイルはロックされたままになります。 例外がスローされても finally
ブロックは実行されるため、上の例の finally
ブロックではファイルを適切に閉じて、エラーを回避できます。
例外がスローされた後、対応する catch
ブロックが呼び出し履歴に見つからない場合は、次のいずれかが発生します。
- 例外がファイナライザーの内部で発生した場合、ファイナライザーは中止され、基本ファイナライザー (存在する場合) が呼び出されます。
- 呼び出し履歴に静的コンストラクターまたは静的フィールド初期化子が含まれている場合は、TypeInitializationException がスローされ、新しい例外の InnerException プロパティに元の例外が割り当てられます。
- スレッドの開始位置に到達すると、スレッドは終了します。
.NET