WPF Dispatcher.BeginInvokeとDispatcher.InvokeAsyncの違い

このページの目次
  1. はじめに
  2. BeginInvokeメソッド
  3. InvokeAsyncメソッド
  4. まとめ

はじめに

.NET 4.5でDispatcherクラスにInvokeAsyncメソッドが追加されました。このメソッドは、従来のBeginInvokeメソッドと似ていますが、例外の扱いが大きく異なっています。そのため、単純に古いBeginInvokeメソッドの代わりに新しいInvokeAsyncメソッドを使用すれば良いというわけではなく、例外の処理方法によって使い分ける必要があります。

BeginInvokeメソッド

BeginInvokeメソッドでは、引数で渡されたデリゲートが例外をスローすると、その例外は、Dispatcher.UnhandledExceptionイベントApplication.DispatcherUnhandledExceptionイベントのハンドラに通知されます。さらに、これらのイベントで例外がハンドルされなかった場合には、AppDomain.UnhandledExceptionイベントで例外が通知されます。

BeginInvokeメソッドは戻り値を介した例外の通知は行いません。そのため、以下のコードのようにawaitを記述しても例外はキャッチできません。

// 間違った例
async Task Example(Dispatcher diapatcher)
{
    try
    {
        await diapatcher.BeginInvoke((Action)(() =>
        {
            // 例外はDispatcher.UnhandledExceptionイベントなどのハンドラに通知される。
            throw new InvalidOperationException("error");
        }));
    }
    catch (Exception ex)
    {
        // ここでは例外をキャッチできない。
    }
}

InvokeAsyncメソッド

InvokeAsyncメソッドでは、引数で渡されたデリゲートが例外をスローしてもDispatcher.UnhandledExceptionイベントなどのイベントは発行されません。代わりに、戻り値を介して例外が通知されます。

// InvokeAsyncメソッドでの例外処理
async Task Example(Dispatcher diapatcher)
{
    try
    {
        await diapatcher.InvokeAsync(() =>
        {
            throw new InvalidOperationException("error");
        });
    }
    catch (Exception ex)
    {
        // ここで例外をキャッチできる。
    }
}

なお、以下のコードのようにawaitを記述し忘れると例外が呼び出し元に通知されません。そのため、try/catchを記述しても例外はキャッチできません。

// 間違った例
void Example(Dispatcher diapatcher)
{
    try
    {
        // awaitが無いので例外が呼び出し元に通知されない。
        diapatcher.InvokeAsync(() =>
        {
            throw new InvalidOperationException("error");
        });
    }
    catch (Exception ex)
    {
        // ここでは例外をキャッチできない。
    }
}

まとめ

BeginInvokeメソッドでは、引数で渡されたデリゲートがスローした例外はDispatcher.UnhandledExceptionイベントやAppDomain.UnhandledExceptionイベントとして通知されます。一方、InvokeAsyncメソッドでは、スローされた例外は戻り値を介して呼び出し元に通知されます。

よって、呼び出し元に例外を返すのが難しい非同期メソッド(戻りがvoidである非同期メソッド)ではBeginInvokeメソッドを使用します。一方、呼び出し元に例外を通知する非同期メソッド(戻りがvoidではない非同期メソッド)の場合にはInvokeAsyncメソッドを使用します。