90 votes

Une bonne solution pour qui les attendent dans try/catch/finally?

J'ai besoin de faire appel à un async méthode de catch - bloc avant de le lancer à nouveau l'exception (avec son stack trace) comme ceci :

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

Mais, malheureusement, vous ne pouvez pas utiliser await en catch ou finally bloc. J'ai appris que c'est parce que le compilateur n'a aucun moyen de revenir en arrière dans un catch bloc à exécuter ce qui est après votre await instruction ou quelque chose comme ça...

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

Puisque je ne peux pas changer l' async méthodes, ni je ne sais si ils utilisent ConfigureAwait(false), j'ai créé ces méthodes qui prennent un Func<Task> qui démarre une méthode async une fois que nous sommes sur un thread différent (pour éviter une situation de blocage) et attend son achèvement:

public static void AwaitTaskSync(Func<Task> action)
{
    Task.Run(async () => await action().ConfigureAwait(false)).Wait();
}

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

public static void AwaitSync(Func<IAsyncAction> action)
{
    AwaitTaskSync(() => action().AsTask());
}

public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action)
{
    return AwaitTaskSync(() => action().AsTask());
}

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

Bien sûr, si vous avez des améliorations ou en connaissez une meilleure approche, je suis à l'écoute! :)

170voto

hvd Points 42125

Vous pouvez déplacer la logique à l'extérieur de l' catch bloquer et renvoyer l'exception après, si nécessaire, en utilisant 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 l'exception de l' StackTrace de la propriété, il reste des enregistrements où à l'intérieur d' TaskThatFails , elle a été lancée.

16voto

Stephen Cleary Points 91731

Si vous devez utiliser async des gestionnaires d'erreur, je vous recommande de quelque chose comme ceci:

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

if (exception != null)
{
  ...
}

Le problème de façon synchrone avec blocage sur async code (indépendamment de ce thread, il est exécuté sur), c'est que vous êtes en mode synchrone bloquant. Dans la plupart des scénarios, il est préférable d'utiliser await.

Mise à jour: Depuis que vous avez besoin de renvoyer, vous pouvez utiliser ExceptionDispatchInfo.

3voto

mrts Points 472

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

public static class TryWithAwaitInCatch
{
    public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync,
        Func<Exception, Task<bool>> errorHandlerAsync)
    {
        ExceptionDispatchInfo capturedException = null;
        try
        {
            await actionAsync();
        }
        catch (Exception ex)
        {
            capturedException = ExceptionDispatchInfo.Capture(ex);
        }

        if (capturedException != null)
        {
            bool needsThrow = await errorHandlerAsync(capturedException.SourceException);
            if (needsThrow)
            {
                capturedException.Throw();
            }
        }
    }
}

On pourrait l'utiliser 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 naming, nous avons gardé volontairement détaillé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X