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

try ブロックは、例外の影響を受ける可能性があるコードを区分化するために、 C# プログラマによって使用されます。 関連付けられた catch ブロックは、スローされた例外を処理するために使用されます。 finally ブロックには、try ブロックで例外がスローされたかどうかにかかわらず実行されるコードが含まれます (try ブロックに割り当てられたリソースの解放など)。 try ブロックには、1 つ以上の catch ブロック、finally ブロック、またはその両方が関連付けられる必要があります。

次のコードは、try-catch ステートメント、try-finally ステートメント、および try-catch-finally ステートメントの例です。

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

try ブロックに catch または finally ブロックがない場合は、コンパイル エラーが発生します。

catch ブロック

catch ブロックでは、キャッチする例外の種類を指定できます。 この型指定は、例外フィルターと呼ばれます。 例外の種類は、Exception から派生している必要があります。 一般に、try ブロックからスローされる可能性があるすべての例外の処理方法を把握している場合や、catch ブロックの末尾に throw ステートメントが含まれている場合を除いては、例外フィルターとして Exception を指定しないでください。

複数の catch ブロックに異なる例外クラスが含まれている場合は、それらを連結することができます。 catch ブロックはコード内で上から下に評価されますが、スローされた各例外に対して実行される catch ブロックは 1 つだけです。 スローされた例外の厳密な型か、その基底クラスを指定する最初の catch ブロックが実行されます。 一致する例外クラスを指定した catch ブロックがない場合は、種類のない catch ブロックが選択されます (それがステートメント内に存在する場合)。 最初に配置する catch ブロックでは、最も具体的な (つまり、最も派生した) 例外クラスを指定することが重要です。

次の条件に該当する場合は、例外をキャッチします。

  • 例外がスローされる理由を十分に理解していて、かつ特定の回復手段を実装できる (FileNotFoundException オブジェクトをキャッチした場合に、ユーザーに新しいファイル名を入力するよう求めるなど)。
  • より具体的な例外を新規に作成し、スローできる。
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • 例外を部分的に処理してから、さらに処理するために渡す必要がある。 次の例では、例外を再スローする前に、エラー ログにエントリを追加する目的で catch ブロックが使用されています。
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

"例外フィルター" を指定して、catch 句にブール式を追加することもできます。 例外フィルターは、特定の catch 句が、その条件が true の場合にのみ一致することを示します。 次の例では、両方の catch 句で同じ例外クラスが使用されますが、追加の条件が確認され、別のエラー メッセージが作成されます。

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

常に false を返す例外フィルターを使用すると、すべての例外を検証できますが、処理は行われません。 通常は、例外をログに記録するために使用します。

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

LogException メソッドにより常に false が返され、この例外フィルターを使用する catch 句は一致しません。 catch 句は一般的なもので (System.Exception を使用)、後の句によってより具体的な例外クラスを処理することができます。

Finally ブロック

finally ブロックでは、try ブロックで実行されるアクションをクリーンアップすることができます。 finally ブロック (存在する場合) は、最後 (try ブロックおよび一致する catch ブロックの後) に実行されます。 finally ブロックは、例外がスローされたかどうか、または例外の種類に一致する catch ブロックが見つかったかどうかにかかわらず、常に実行されます。

finally ブロックは、ランタイム内のガベージ コレクターによってオブジェクトがファイナライズされるのを待つことなく、ファイル ストリーム、データベース接続、グラフィックス ハンドルなどのリソースを解放するために使用できます。

次の例では、try ブロックで開かれたファイルを閉じるために finally ブロックが使用されています。 ファイルを閉じる前に、ファイル ハンドルの状態が確認されています。 try ブロックからファイルを開けなかった場合は、ファイル ハンドルの値が null のままになり、finally ブロックでファイルが閉じられることはありません。 代わりに、try ブロックでファイルが正常に開かれた場合は、finally ブロックによって開かれたファイルが閉じられます。

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

C# 言語仕様

詳細については、「C# 言語仕様」の例外try ステートメントに関するセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。

関連項目