例外処理ステートメント - throwtry-catchtry-finallytry-catch-finally

例外を処理するには、throw および try ステートメントを使用してください。 例外をスローするには、throw ステートメントを使用してください。 コード ブロックの実行中に発生する可能性がある例外をキャッチして処理するには、try ステートメントを使用してください。

throw ステートメント

throw ステートメントにより例外がスローされます。

if (shapeAmount <= 0)
{
    throw new ArgumentOutOfRangeException(nameof(shapeAmount), "Amount of shapes must be positive.");
}

throw e; ステートメントでは、e の結果は System.Exception に暗黙に変換される必要があります。

組み込みの例外クラス (ArgumentOutOfRangeExceptionInvalidOperationException など) を使用できます。 .NET には、特定の条件で例外をスローするヘルパー メソッド (ArgumentNullException.ThrowIfNull および ArgumentException.ThrowIfNullOrEmpty) も用意されています。 System.Exception から派生する独自の例外クラスを定義することもできます。 詳細については、「例外の作成とスロー」をご覧ください。

catch ブロック内では、throw; ステートメントを使用して、catch ブロックによって処理される例外を再スローできます。

try
{
    ProcessShapes(shapeAmount);
}
catch (Exception e)
{
    LogError(e, "Shape processing failed.");
    throw;
}

Note

throw; は、Exception.StackTrace プロパティに格納されている例外の元のスタック トレースを保持します。 反対に、throw e;eStackTrace プロパティを更新します。

例外がスローされると、共通言語ランタイム (CLR) によって、この例外を処理できる catch ブロック が検索されます。 現在実行されているメソッドにそのような catch ブロックが含まれていない場合、CLR は現在のメソッドを呼び出したメソッドなどを呼び出し履歴の上まで調べます。 catch ブロックが見つからない場合、CLR は実行中のスレッドを終了します。 詳細については、C# 言語仕様の「例外の処理方法」セクションを参照してください。

throw

throw は、式として使用することもできます。 これは、次に示すような多くのケースで役立ちます。

  • 条件演算子。 次の例では、throw 式を使用して、渡された配列 args が空の場合に ArgumentException をスローします。

    string first = args.Length >= 1 
        ? args[0]
        : throw new ArgumentException("Please supply at least one argument.");
    
  • Null 合体演算子。 次の例では、プロパティに割り当てる文字列が null の場合、throw 式を使用して ArgumentNullException をスローします。

    public string Name
    {
        get => name;
        set => name = value ??
            throw new ArgumentNullException(paramName: nameof(value), message: "Name cannot be null");
    }
    
  • 式形式のラムダまたはメソッド。 次の例では、throw 式を使用して InvalidCastException をスローし、DateTime 値への変換がサポートされていないことを示します。

    DateTime ToDateTime(IFormatProvider provider) =>
             throw new InvalidCastException("Conversion to a DateTime is not supported.");
    

try ステートメント

try ステートメントは、次のいずれかの形式で使用できます。try-catch - try ブロック内のコードの実行中に発生する可能性がある例外を処理する、try-finally - コントロールが try ブロックを離れたときに実行されるコードを指定する、try-catch-finally - 前の 2 つのフォームの組み合わせ。

try-catch ステートメント

コード ブロックの実行中に発生する可能性がある例外を処理するには、try-catch ステートメントを使用してください。 try ブロック内で例外が発生する可能性があるコードを配置します。 対応する catch ブロックで処理する例外の基本データ型を指定するには、"catch 句" を使用してください。

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

いくつかの catch 句を指定できます。

try
{
    var result = await ProcessAsync(-3, 4, cancellationToken);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (ArgumentException e)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Processing is cancelled.");
}

例外が発生すると、catch 句は指定した順序で上から下に調べられます。 スローされた例外に対して最大で 1 つの catch ブロックのみが実行されます。 前の例にも示されているように、例外変数の宣言を省略し、catch 句で例外の種類のみを指定できます。 例外の種類が指定されていない catch 句は、すべての例外と一致し、存在する場合は最後の catch 句である必要があります。

キャッチされた例外を再スローする場合は、次の例に示すように、throw ステートメントを使用してください。

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e)
{
    LogError(e, "Processing failed.");
    throw;
}

Note

throw; は、Exception.StackTrace プロパティに格納されている例外の元のスタック トレースを保持します。 反対に、throw e;eStackTrace プロパティを更新します。

when 例外フィルター

例外の種類と共に、例外フィルターを指定することもできます。これは、例外をさらに調べて、対応する catch ブロックがその例外を処理するかどうかを決定します。 次の例に示すように、例外フィルターは、when キーワードに続くブール式です。

try
{
    var result = Process(-3, 4);
    Console.WriteLine($"Processing succeeded: {result}");
}
catch (Exception e) when (e is ArgumentException || e is DivideByZeroException)
{
    Console.WriteLine($"Processing failed: {e.Message}");
}

前の例では、例外フィルターを使用して、指定された 2 つの型の例外を処理する 1 つの catch ブロックを指定しています。

例外フィルターで区別する場合は、同じ例外の種類に対して複数 catch の句を指定できます。 これらの句の 1 つに例外フィルターがない場合があります。 このような句が存在する場合は、その例外の種類を指定する句の最後である必要があります。

catch 句に例外フィルターがある場合は、その後に表示される catch 句の例外型と同じかそれ以下の派生の例外型を指定できます。 たとえば、例外フィルターが存在する場合、catch (Exception e) 句を最後の句にする必要はありません。

非同期および反復子メソッドの例外

次の例に示すように、非同期関数で例外が発生した場合、関数の結果を待機すると、関数の呼び出し元に伝達されます。

public static async Task Run()
{
    try
    {
        Task<int> processing = ProcessAsync(-1);
        Console.WriteLine("Launched processing.");

        int result = await processing;
        Console.WriteLine($"Result: {result}.");
    }
    catch (ArgumentException e)
    {
        Console.WriteLine($"Processing failed: {e.Message}");
    }
    // Output:
    // Launched processing.
    // Processing failed: Input must be non-negative. (Parameter 'input')
}

private static async Task<int> ProcessAsync(int input)
{
    if (input < 0)
    {
        throw new ArgumentOutOfRangeException(nameof(input), "Input must be non-negative.");
    }

    await Task.Delay(500);
    return input;
}

反復子メソッドで例外が発生した場合、反復子が次の要素に進むときにのみ呼び出し元に伝達されます。

try-finally ステートメント

try-finally ステートメントでは、コントロールが try ブロックを離れると、finally ブロックが実行されます。 コントロールは、次の結果として try ブロックを離れる可能性があります。

次の例では、finally ブロックを使用して、コントロールがメソッドを離れる前にオブジェクトの状態をリセットします。

public async Task HandleRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    finally
    {
        Busy = false;
    }
}

finally ブロックを使用して、try ブロックで使用される割り当て済みリソースをクリーン アップすることもできます。

Note

リソースの型が IDisposable または IAsyncDisposable インターフェイスを実装する場合は、using ステートメントを検討してください。 using ステートメントを使用すると、制御が using ステートメントを離れるときに、取得したリソースが確実に破棄されます。 コンパイラは、using ステートメントを try-finally ステートメントに変換します。

finally ブロックの実行は、例外のアンワインド操作がオペレーティング システムによってトリガーされるかどうかに依存します。 finally ブロックが実行されない唯一のケースは、プログラムの即時終了を伴うものです。 たとえば、このような終了は、Environment.FailFast 呼び出しや OverflowException または InvalidProgramException 例外が原因で発生する場合があります。 ほとんどのオペレーティング システムは、プロセスの停止とアンロードの一環として、リソースの適切なクリーンアップが行われます。

try-catch-finally ステートメント

try ブロックの実行中に発生する可能性がある例外の処理、および try ステートメントからコントロールが離れたときに実行する必要があるコードの指定の両方に try-catch-finally ステートメントを使います。

public async Task ProcessRequest(int itemId, CancellationToken ct)
{
    Busy = true;

    try
    {
        await ProcessAsync(itemId, ct);
    }
    catch (Exception e) when (e is not OperationCanceledException)
    {
        LogError(e, $"Failed to process request for item ID {itemId}.");
        throw;
    }
    finally
    {
        Busy = false;
    }

}

catch ブロックによって例外が処理されると、finally ブロックはその catch ブロックの実行後に実行されます (catch ブロックの実行中に別の例外が発生した場合でも)。 catch および finally ブロックの詳細については、それぞれ「try-catch ステートメント」および「try-finallyステートメント」を参照してください。

C# 言語仕様

詳細については、「C# 言語仕様」の次のセクションを参照してください。

関連項目