Saya perlu memanggil async
metode dalam satu catch
blok sebelum melemparkan lagi pengecualian (dengan jejak tumpukannya) seperti ini:
try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
Tapi sayangnya Anda tidak dapat menggunakan await
dalam catch
atau finally
blok. Saya mempelajarinya karena kompiler tidak memiliki cara untuk kembali catch
ke blok untuk mengeksekusi apa yang ada setelah await
instruksi Anda atau sesuatu seperti itu ...
Saya mencoba menggunakan Task.Wait()
untuk mengganti await
dan saya menemui jalan buntu. Saya mencari di Web bagaimana saya dapat menghindari ini dan menemukan situs ini .
Karena saya tidak dapat mengubah async
metode dan juga tidak tahu apakah mereka menggunakannya ConfigureAwait(false)
, saya membuat metode ini yang mengambil Func<Task>
yang memulai metode asinkron setelah kita berada di utas yang berbeda (untuk menghindari kebuntuan) dan menunggu penyelesaiannya:
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());
}
Jadi pertanyaan saya adalah: Apakah menurut Anda kode ini baik-baik saja?
Tentu saja, jika Anda memiliki beberapa penyempurnaan atau mengetahui pendekatan yang lebih baik, saya mendengarkan! :)
sumber
await
blok tangkap sebenarnya diperbolehkan sejak C # 6.0 (lihat jawaban saya di bawah)Jawaban:
Anda bisa memindahkan logika ke luar
catch
blok dan memunculkan kembali pengecualian setelah, jika perlu, dengan menggunakanExceptionDispatchInfo
.static async Task f() { ExceptionDispatchInfo capturedException = null; try { await TaskThatFails(); } catch (MyException ex) { capturedException = ExceptionDispatchInfo.Capture(ex); } if (capturedException != null) { await ExceptionHandler(); capturedException.Throw(); } }
Dengan cara ini, saat pemanggil memeriksa
StackTrace
properti pengecualian , ia masih mencatat di mana diTaskThatFails
dalamnya itu dilemparkan.sumber
ExceptionDispatchInfo
daripadaException
(seperti dalam jawaban Stephen Cleary)?Exception
, Anda kehilangan semua sebelumnyaStackTrace
?Anda harus tahu bahwa sejak C # 6.0, itu mungkin untuk digunakan
await
dalamcatch
danfinally
blok, jadi Anda sebenarnya bisa melakukan ini:try { // Do something } catch (Exception ex) { await DoCleanupAsync(); throw; }
Fitur C # 6.0 baru, termasuk yang baru saja saya sebutkan , tercantum di sini atau sebagai video di sini .
sumber
Jika Anda perlu menggunakan
async
penangan kesalahan, saya merekomendasikan sesuatu seperti ini:Exception exception = null; try { ... } catch (Exception ex) { exception = ex; } if (exception != null) { ... }
Masalah dengan memblokir
async
kode secara sinkron (terlepas dari utas apa yang menjalankannya) adalah Anda memblokir secara sinkron. Dalam kebanyakan skenario, lebih baik digunakanawait
.Pembaruan: Karena Anda perlu memutar ulang, Anda dapat menggunakan
ExceptionDispatchInfo
.sumber
throw exception;
dalamif
pernyataan, jejak tumpukan akan hilang.Kami mengekstrak jawaban bagus hvd untuk kelas utilitas yang dapat digunakan kembali berikut dalam proyek kami:
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(); } } } }
Seseorang akan menggunakannya sebagai berikut:
public async Task OnDoSomething() { await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync( async () => await DoSomethingAsync(), async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; } ); }
Jangan ragu untuk meningkatkan penamaan, kami menyimpannya dengan sengaja. Perhatikan bahwa tidak perlu menangkap konteks di dalam pembungkus karena sudah ditangkap di situs panggilan, karenanya
ConfigureAwait(false)
.sumber