次のように(スタックトレースと共に)再び例外をスローする前に、async
ブロックでcatch
メソッドを呼び出す必要があります。
_try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
_
ただし、残念ながら、await
またはcatch
ブロックでfinally
を使用することはできません。コンパイラがcatch
命令のようなものを実行するためにawait
ブロックに戻る方法がないためだとわかった...
Task.Wait()
を使用してawait
を置き換えようとしましたが、デッドロックが発生しました。これを回避する方法をWebで検索し、 このサイト を見つけました。
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());
}
_
だから私の質問は次のとおりです。このコードは大丈夫だと思いますか?
もちろん、あなたがいくつかの機能強化を持っているか、より良いアプローチを知っているなら、私は聞いています! :)
必要に応じて catch
を使用することで、ロジックを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();
}
}
この方法では、呼び出し元が例外のStackTrace
プロパティを検査するときに、TaskThatFails
の内部でスローされた場所を記録します。
C#6.0以降、await
およびcatch
ブロックでfinally
を使用できることを知っておく必要があるため、実際にこれを行うことができます。
try
{
// Do something
}
catch (Exception ex)
{
await DoCleanupAsync();
throw;
}
先ほど触れたものを含む新しいC#6.0の機能 ここにリストされています またはビデオとして ここ 。
async
エラーハンドラを使用する必要がある場合は、次のようなものをお勧めします。
Exception exception = null;
try
{
...
}
catch (Exception ex)
{
exception = ex;
}
if (exception != null)
{
...
}
async
コードの同期ブロックの問題は(実行しているスレッドに関係なく)、同期ブロックしていることです。ほとんどのシナリオでは、await
を使用することをお勧めします。
更新:再スローする必要があるため、ExceptionDispatchInfo
を使用できます。
プロジェクトの次の再利用可能なユーティリティクラスに hvd's great answer を抽出しました。
_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)
です。