다음 과 같이 예외 (스택 추적 포함)를 다시 던지기 전에 블록 async
에서 메서드 를 호출해야합니다 catch
.
try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
그러나 불행히도 당신은 또는 블록 await
에서 사용할 수 없습니다 . 나는 컴파일러가 당신의 명령이나 그와 비슷한 것을 실행하기 위해 블록으로 돌아갈 방법이 없기 때문에 배웠습니다 …catch
finally
catch
await
Task.Wait()
교체 에 사용하려고했는데 await
교착 상태가 발생했습니다. 나는 이것을 피할 수있는 방법을 웹에서 검색했고이 사이트를 찾았다 .
async
메서드를 변경할 수 없고을 사용하는지 알 수 없기 때문에 다른 스레드 (교착 상태를 피하기 위해)에있을 때 비동기 메서드를 시작하고 완료를 기다리는 ConfigureAwait(false)
a 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
블록 외부로 이동하고 필요한 경우 예외를 다시 throw 할 수 있습니다 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
in 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의 훌륭한 답변 을 추출 했습니다 .
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)
.