95

次のように、(スタック トレースを含む) 例外を再度スローする前asyncに、ブロック内のメソッドを呼び出す必要があります。catch

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

ただし、残念ながらorブロックawaitでは使用できません。コンパイラには、ブロックに戻って命令の後にあるものなどを実行する方法がないためだとわかりました...catchfinallycatchawait

Task.Wait()交換しようとしawaitたら行き詰まりました。これを回避する方法を Web で検索したところ、このサイトを見つけました。

メソッドを変更することも、asyncメソッドが を使用しているかどうかもわからないため、(デッドロックを回避するために) 別のスレッドにいるときに async メソッドを開始し、その完了を待機するConfigureAwait(false)メソッドを作成しました。Func<Task>

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());
}

私の質問は次のとおりです。このコードは大丈夫だと思いますか?

もちろん、機能強化があれば、またはより良いアプローチを知っている場合は、聞いています! :)

4

4 に答える 4

55

C# 6.0 以降ではawaitincatchfinallyブロックを使用できるため、実際には次のようにできます。

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

C# 6.0 の新機能は、先ほど説明したものを含めて、ここにリストされているか、ビデオとしてここにリストされています。

于 2014-11-15T17:05:24.083 に答える
3

プロジェクトの次の再利用可能なユーティリティ クラスに対する hvd の優れた回答を抽出しました。

public static class TryWithAwaitInCatch
{
    public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync,
        Func<Exception, Task<bool>> 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();
            }
        }
    }
}

次のように使用します。

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

ネーミングを自由に改善してください。意図的に冗長にしています。呼び出しサイトで既にキャプチャされているため、ラッパー内のコンテキストをキャプチャする必要がないことに注意してくださいConfigureAwait(false)

于 2014-11-11T14:35:41.733 に答える