Une bonne solution pour attendre dans try / catch / finally?

Je dois appeler une méthode async dans un bloc catch avant de lancer à nouveau l’exception (avec sa trace de stack) comme ceci:

 try { // Do something } catch { // <- Clean things here with async methods throw; } 

Mais malheureusement, vous ne pouvez pas utiliser dans un bloc catch ou finally . J’ai appris que c’est parce que le compilateur n’a aucun moyen de revenir dans un bloc catch pour exécuter ce qui suit votre instruction d’ await ou quelque chose du genre …

J’ai essayé d’utiliser Task.Wait() pour remplacer Task.Wait() et j’ai eu un blocage. J’ai cherché sur le Web comment je pouvais éviter cela et j’ai trouvé ce site .

Comme je ne peux pas changer les méthodes async et si elles utilisent ConfigureAwait(false) , j’ai créé ces méthodes qui prennent un Func qui démarre une méthode asynchrone une fois que nous sums sur un thread différent (pour éviter un blocage) et attend son achèvement:

 public static void AwaitTaskSync(Func action) { Task.Run(async () => await action().ConfigureAwait(false)).Wait(); } public static TResult AwaitTaskSync(Func<Task> action) { return Task.Run(async () => await action().ConfigureAwait(false)).Result; } public static void AwaitSync(Func action) { AwaitTaskSync(() => action().AsTask()); } public static TResult AwaitSync(Func<IAsyncOperation> action) { return AwaitTaskSync(() => action().AsTask()); } 

Donc mes questions sont: Pensez-vous que ce code est correct?

Bien sûr, si vous avez des améliorations ou que vous connaissez une meilleure approche, j’écoute! 🙂

Vous pouvez déplacer la logique en dehors du bloc catch et repasser l’exception, si nécessaire, à l’aide d’ ExceptionDispatchInfo .

 static async Task f() { ExceptionDispatchInfo capturedException = null; try { await TaskThatFails(); } catch (MyException ex) { capturedException = ExceptionDispatchInfo.Capture(ex); } if (capturedException != null) { await ExceptionHandler(); capturedException.Throw(); } } 

De cette façon, lorsque l’appelant inspecte la propriété StackTrace l’exception, il enregistre toujours l’emplacement dans lequel TaskThatFails a été lancé.

Vous devriez savoir que depuis C # 6.0, il est possible d’utiliser await in catch et finally , vous pouvez donc faire ceci:

 try { // Do something } catch (Exception ex) { await DoCleanupAsync(); throw; } 

Les nouvelles fonctionnalités de C # 6.0, y compris celle que je viens de mentionner, sont listées ici ou sous forme de vidéo ici .

Si vous avez besoin d’utiliser des gestionnaires d’erreur async , je vous recommande quelque chose comme ceci:

 Exception exception = null; try { ... } catch (Exception ex) { exception = ex; } if (exception != null) { ... } 

Le problème avec le blocage synchrone du code async (quel que soit le thread sur lequel il s’exécute) est le blocage synchrone. Dans la plupart des scénarios, il vaut mieux utiliser en await .

Mise à jour: Comme vous devez relancer, vous pouvez utiliser ExceptionDispatchInfo .

Nous avons extrait la réponse de hvd à la classe utilitaire réutilisable suivante dans notre projet:

 public static class TryWithAwaitInCatch { public static async Task ExecuteAndHandleErrorAsync(Func actionAsync, Func> errorHandlerAsync) { ExceptionDispatchInfo capturedException = null; try { await actionAsync().ConfigureAwait(false); } catch (Exception ex) { capturedException = ExceptionDispatchInfo.Capture(ex); } if (capturedException != null) { bool needsThrow = await errorHandlerAsync(capturedException.SourceException).ConfigureAwait(false); if (needsThrow) { capturedException.Throw(); } } } } 

On l’utiliserait comme suit:

  public async Task OnDoSomething() { await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync( async () => await DoSomethingAsync(), async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; } ); } 

N’hésitez pas à améliorer le nom, nous l’avons gardé intentionnellement verbeux. Notez qu’il n’est pas nécessaire de capturer le contexte à l’intérieur du wrapper car il est déjà capturé dans le site d’appel, donc ConfigureAwait(false) .