.NET 4.5では、 WPF Dispatcher が InvokeAsync と呼ばれるDispatcherのスレッドで処理を実行する新しいメソッドセットを取得したことに気付きました。以前、.NET 4.5には Invoke と BeginInvoke があり、それぞれこれを同期的および非同期的に処理していました。
命名と利用可能なわずかに異なるオーバーロードに加えて、BeginInvoke
メソッドとInvokeAsync
メソッドの間に大きな違いはありますか?
ああ、私はすでにチェックしました、両方ともawait
edにすることができます:
private async Task RunStuffOnUiThread(Action action)
{
// both of these works fine
await dispatcher.BeginInvoke(action);
await dispatcher.InvokeAsync(action);
}
BeginInvoke
メソッドはprivate LegacyBeginInvokeImpl
メソッドを呼び出しますが、このメソッドはprivateメソッドInvokeAsyncImpl
(InvokeAsync
で使用されるメソッド)を呼び出します。基本的に同じことです。単純なリファクタリングのように見えますが、BeginInvoke
メソッドが古いものとしてフラグ付けされていなかったのは奇妙です。
BeginInvoke:
public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method)
{
return this.LegacyBeginInvokeImpl(priority, method, null, 0);
}
private DispatcherOperation LegacyBeginInvokeImpl(DispatcherPriority priority, Delegate method, object args, int numArgs)
{
Dispatcher.ValidatePriority(priority, "priority");
if (method == null)
{
throw new ArgumentNullException("method");
}
DispatcherOperation dispatcherOperation = new DispatcherOperation(this, method, priority, args, numArgs);
this.InvokeAsyncImpl(dispatcherOperation, CancellationToken.None);
return dispatcherOperation;
}
InvokeAsync:
public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority)
{
return this.InvokeAsync(callback, priority, CancellationToken.None);
}
public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
if (callback == null)
{
throw new ArgumentNullException("callback");
}
Dispatcher.ValidatePriority(priority, "priority");
DispatcherOperation dispatcherOperation = new DispatcherOperation(this, priority, callback);
this.InvokeAsyncImpl(dispatcherOperation, cancellationToken);
return dispatcherOperation;
}
例外処理は異なります。
以下を確認することをお勧めします。
private async void OnClick(object sender, RoutedEventArgs e)
{
Dispatcher.UnhandledException += OnUnhandledException;
try
{
await Dispatcher.BeginInvoke((Action)(Throw));
}
catch
{
// The exception is not handled here but in the unhandled exception handler.
MessageBox.Show("Catched BeginInvoke.");
}
try
{
await Dispatcher.InvokeAsync((Action)Throw);
}
catch
{
MessageBox.Show("Catched InvokeAsync.");
}
}
private void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show("Catched UnhandledException");
}
private void Throw()
{
throw new Exception();
}
メソッドのシグネチャには違いがあります。
_BeginInvoke(Delegate, Object[])
InvokeAsync(Action)
_
BeginInvoke()
の場合、コンパイラは暗黙的に配列_Object[]
_を作成しますが、InvokeAsync()
の場合、このような配列は必要ありません。
_IL_0001: ldarg.0
IL_0002: call instance class [WindowsBase]System.Windows.Threading.Dispatcher [WindowsBase]System.Windows.Threading.DispatcherObject::get_Dispatcher()
IL_0007: ldarg.1
IL_0008: ldc.i4.0
IL_0009: newarr [mscorlib]System.Object
IL_000e: callvirt instance class [WindowsBase]System.Windows.Threading.DispatcherOperation [WindowsBase]System.Windows.Threading.Dispatcher::BeginInvoke(class [mscorlib]System.Delegate, object[])
IL_0014: ldarg.0
IL_0015: call instance class [WindowsBase]System.Windows.Threading.Dispatcher [WindowsBase]System.Windows.Threading.DispatcherObject::get_Dispatcher()
IL_001a: ldarg.1
IL_001b: callvirt instance class [WindowsBase]System.Windows.Threading.DispatcherOperation [WindowsBase]System.Windows.Threading.Dispatcher::InvokeAsync(class [mscorlib]System.Action)
_
さて、私が気づいた違いの1つは、InvokeAsyncには、戻り値としてDispatcherOperationを返し、デリゲート入力パラメーターとしてFuncを受け入れる汎用オーバーロードがあることです。したがって、タスクの結果を待つ方法に似たタイプセーフな方法で、InvokeAsyncを介して操作の結果を取得できます。