Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

C#辞典

  1. トップページ
  2. C#辞典
  3. 例外処理と非同期(try/catch with async)

例外処理と非同期(try/catch with async)

非同期メソッド(async / await)の中で例外を適切に捕捉・処理するための『try / catch / finally』の使い方と、非同期特有の注意点を解説します。

構文
using System;
using System.Threading.Tasks;

// 非同期メソッド内で try/catch を使う基本構文です。
static async Task 非同期メソッド()
{
    try
    {
        await 非同期処理();
    }
    catch (例外型 ex)
    {
        // 例外を処理します。
    }
    finally
    {
        // 例外の有無に関わらず必ず実行されます。
    }
}

// Task.WhenAll() で複数タスクを並列実行する際は AggregateException に注意します。
try
{
    await Task.WhenAll(task1, task2, task3);
}
catch (Exception ex)
{
    // WhenAll では最初の例外のみ catch されます。
}
メソッド一覧
構文 / プロパティ概要
try { await ... } catch (...)await 式を try ブロックに含めることで、非同期処理の例外を通常の catch で捕捉できます。
AggregateExceptionTask.WhenAll() など複数タスクが失敗した場合にスローされる、複数例外を束ねた例外型です。
Task.Exceptionタスクが失敗した場合に AggregateException を格納するプロパティです。
OperationCanceledExceptionCancellationToken によるキャンセルが発生した場合にスローされる例外です。
サンプルコード
using System;
using System.Threading.Tasks;

class Program
{
    // 例外をスローする非同期メソッドです。
    static async Task<int> 割り算Async(int 分子, int 分母)
    {
        await Task.Delay(100); // 非同期処理のシミュレーションです。
        if (分母 == 0)
            throw new DivideByZeroException("ゼロで割ることはできません。");
        return 分子 / 分母;
    }

    // 複数の Task を並列実行して例外を処理します。
    static async Task 並列実行サンプル()
    {
        Task<int> task1 = 割り算Async(10, 2);
        Task<int> task2 = 割り算Async(20, 0); // こちらは例外が発生します。
        Task<int> task3 = 割り算Async(30, 3);

        // すべてのタスクを待機します。
        Task<int[]> whenAll = Task.WhenAll(task1, task2, task3);

        try
        {
            int[] 結果 = await whenAll;
        }
        catch
        {
            // AggregateException の内部例外を列挙します。
            if (whenAll.Exception != null)
            {
                foreach (Exception ex in whenAll.Exception.InnerExceptions)
                {
                    Console.WriteLine($"例外: {ex.Message}");
                }
            }
        }
    }

    static async Task Main()
    {
        // 基本的な try/catch/finally の使い方です。
        try
        {
            int 結果 = await 割り算Async(10, 2);
            Console.WriteLine($"結果: {結果}");

            int エラー = await 割り算Async(10, 0); // 例外が発生します。
        }
        catch (DivideByZeroException ex)
        {
            Console.WriteLine($"ゼロ除算エラー: {ex.Message}");
        }
        finally
        {
            Console.WriteLine("finally ブロックが実行されました。");
        }

        // 並列実行のサンプルです。
        await 並列実行サンプル();
    }
}
概要

非同期メソッド内で awaittry ブロックに含めると、非同期処理で発生した例外をそのまま catch で捕捉できます。async void メソッドで発生した例外は catch できず、アプリケーションをクラッシュさせます。イベントハンドラー以外では async void を使わないようにしてください。

『Task.WhenAll()』の場合、await すると複数の例外のうち最初の 1 件しか catch されません。すべての例外を確認するには task.Exception.InnerExceptions を参照してください。例外処理の基礎についてはtry / catch / finallyを、非同期処理の基礎についてはasync / awaitをご確認ください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。