簡體   English   中英

“async void”WPF命令處理程序中的異常處理

[英]Exception handling inside “async void” WPF command handlers

我正在審查我的同事的一些WPF代碼,這是一個基於UserControl的組件 ,包含許多async void事件和命令處理程序。 這些方法目前不在內部實現任何錯誤處理

代碼簡而言之:

<Window.CommandBindings>
    <CommandBinding
        Command="ApplicationCommands.New"
        Executed="NewCommand_Executed"/>
</Window.CommandBindings>
private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    // do some fake async work (and may throw if timeout < -1)
    var timeout = new Random(Environment.TickCount).Next(-100, 100);
    await Task.Delay(timeout);
}

拋出異常但在NewCommand_Executed未觀察到的異常只能在全局級別上處理 (例如,使用AppDomain.CurrentDomain.UnhandledException )。 顯然,這不是一個好主意。

我可以在本地處理異常:

private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    try
    {
        // do some fake async work (throws if timeout < -1)
        var timeout = new Random(Environment.TickCount).Next(-100, 100);
        await Task.Delay(timeout);
    }
    catch (Exception ex)
    {
        // somehow log and report the error
        MessageBox.Show(ex.Message);
    }
}

但是,在這種情況下,主機應用程序的ViewModel將不會意識到 NewCommand_Executed 的錯誤 也不是理想的解決方案,加上錯誤報告UI不應該總是作為庫代碼的一部分。

另一種方法是在本地處理它們並觸發一個專門的錯誤事件:

public class AsyncErrorEventArgs: EventArgs
{
    public object Sender { get; internal set; }
    public ExecutedRoutedEventArgs Args { get; internal set; }
    public ExceptionDispatchInfo ExceptionInfo { get; internal set; }
}

public delegate void AsyncErrorEventHandler(object sender, AsyncErrorEventArgs e);

public event AsyncErrorEventHandler AsyncErrorEvent;

private async void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
    ExceptionDispatchInfo exceptionInfo = null;

    try
    {
        // do some fake async work (throws if timeout < -1)
        var timeout = new Random(Environment.TickCount).Next(-100, 100);
        await Task.Delay(timeout);
    }
    catch (Exception ex)
    {
        // capture the error
        exceptionInfo = ExceptionDispatchInfo.Capture(ex);
    }

    if (exceptionInfo != null && this.AsyncErrorEvent != null)
        this.AsyncErrorEvent(sender, new AsyncErrorEventArgs { 
            Sender = this, Args = e, ExceptionInfo = exceptionInfo });
}

我最喜歡最后一個,但我很欣賞任何其他建議,因為我對WPF的體驗有限。

  • 是否存在已建立的WPF模式以將錯誤從async void命令處理程序傳播到ViewModal?

  • 在WPF命令處理程序中執行異步工作通常是一個壞主意 ,因為它們可能用於快速同步UI更新?

我在WPF的上下文中問這個問題,但我認為它也適用於WinForms中的async void事件處理程序。

這里的問題是您的UserControl庫沒有以典型的MVVM方式構建。 通常,對於非平凡的命令,您的UserControl的代碼不會直接綁定到命令,而是具有在設置(通過綁定到ViewModel)時將觸發控件中的操作的屬性。 然后,您的ViewModel將綁定到應用程序命令,並設置適當的屬性。 (或者,您的MVVM框架可能有另一個消息傳遞方案,可用於ViewModel和View之間的交互)。

至於在UI中拋出的異常,我再次感覺存在架構問題。 如果UserControl做的不僅僅是充當View,(即運行可能導致意外異常的任何類型的業務邏輯),那么這應該分為View和ViewModel。 ViewModel將運行邏輯,可以由其他應用程序ViewModel實例化,也可以通過其他方法進行通信(如上所述)。

如果UserControl的布局/可視化代碼拋出異常,則ViewModel不會以任何方式捕獲(幾乎無例外)。 正如您所提到的,這應該僅由全局級別處理程序進行日志記錄處理。

最后,如果Control的代碼中確實存在已知的“異常”,則需要通知您的ViewModel,我建議捕獲已知異常並引發事件/命令並設置屬性。 但同樣,這不應該用於例外,只是預期的“錯誤”狀態。

在我看來,用戶幾乎100%不知道的異常的傳播並不是一個好習慣。 看到這個

我看到了你真正擁有的兩個選項,因為WPF沒有提供任何開箱即用的機制來通知任何問題:

  1. 你已經提供捕捉和解雇事件的方式。
  2. 從異步方法返回Task對象(在您的情況下,您似乎必須通過屬性公開它)。 用戶將能夠檢查執行期間是否有任何錯誤,並在需要時附加延續任務。 在處理程序內部,您可以捕獲任何異常並使用TaskCompletionSource來設置處理程序的結果。

總而言之,你必須為這樣的代碼編寫一些xml-comments,因為這並不容易理解。 最重要的是你不應該(幾乎)從任何輔助線程拋出任何異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM