J'ai remarqué dans .NET 4.5 que le WPF Dispatcher avait reçu un nouvel ensemble de méthodes pour exécuter des tâches sur le thread du Dispatcher appelé InvokeAsync . Auparavant, .NET 4.5, nous avions Invoke et BeginInvoke qui gérait cela de manière syncroneuse et asynchrone, respectivement.
Outre la dénomination et les surcharges légèrement différentes disponibles, existe-t-il des différences majeures entre les méthodes BeginInvoke
et InvokeAsync
?
Oh, et j'ai déjà vérifié, les deux peuvent être await
ed:
private async Task RunStuffOnUiThread(Action action)
{
// both of these works fine
await dispatcher.BeginInvoke(action);
await dispatcher.InvokeAsync(action);
}
Il n'y a pas de différence puisque la méthode BeginInvoke
appelle une méthode privée LegacyBeginInvokeImpl
, comme itslef appelle la méthode privée InvokeAsyncImpl
(la méthode utilisée par InvokeAsync
). Donc, c'est fondamentalement la même chose. Il semble que ce soit une simple refactorisation, mais il est étrange que les méthodes BeginInvoke
n'aient pas été marquées comme obsolètes.
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;
}
La gestion des exceptions est différente.
Vous voudrez peut-être vérifier les éléments suivants:
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();
}
Il y a une différence dans la signature de la méthode:
BeginInvoke(Delegate, Object[])
InvokeAsync(Action)
Pour BeginInvoke()
le compilateur crée le tableau Object[]
implicitement, alors que pour InvokeAsync()
un tel tableau n’est pas nécessaire
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)
Eh bien, une différence que j'ai remarquée est qu'InvokeAsync a une surcharge générique qui renvoie un DispatcherOperation en tant que valeur de retour et accepte un Func comme paramètre d'entrée délégué. Ainsi, vous pouvez récupérer le résultat de l'opération via InvokeAsync de manière sécurisée au type, de la même manière que vous pouvez attendre le résultat d'une tâche.