【问题标题】:Exception handling for events事件的异常处理
【发布时间】:2011-02-01 05:39:11
【问题描述】:

如果这是一个简单的问题,我深表歉意(我的 Google-Fu 今天可能不好)。

想象一下这个 WinForms 应用程序,它具有这种类型的设计:主应用程序 -> 显示一个对话框 -> 第一个对话框可以显示另一个对话框。两个对话框都有确定/取消按钮(数据输入)。

我试图找出某种类型的全局异常处理,类似于 Application.ThreadException。我的意思是:

每个对话框都有一些事件处理程序。第二个对话框可能有:

private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    try
    {      
        AllSelectedIndexChangedCodeInThisFunction();
    }
    catch(Exception ex)
    {
        btnOK.enabled = false;  // Bad things, let's not let them save
        // log stuff, and other good things
    }
}

真的,这个对话框中的所有事件处理程序都应该以这种方式处理。这是一个例外情况,所以我只想记录所有相关信息,显示一条消息,然后禁用该对话框的确定​​按钮。

但是,我想避免在每个事件处理程序中使用 try/catch(如果可以的话)。所有这些 try/catch 的一个缺点是:

private void someFunction()
{
    // If an exception occurs in SelectedIndexChanged,
    // it doesn't propagate to this function
    combobox.selectedIndex = 3; 
}

我不相信 Application.ThreadException 是一个解决方案,因为我不希望异常一直回落到第一个对话框,然后是主应用程序。我不想关闭应用程序,我只想记录它,显示一条消息,然后让他们取消对话框。他们可以从那里决定做什么(也许去应用程序的其他地方)。

基本上,第一个对话框和第二个对话框之间的“全局处理程序”(然后,我想,主应用程序和第一个对话框之间的另一个“全局处理程序”)。

【问题讨论】:

    标签: c# winforms exception-handling


    【解决方案1】:

    是的,Application.ThreadException 的默认处理是一个错误。不幸的是,这是一个必要的错误,需要不要立即让数十万编写他们的第一个 Windows 窗体应用程序的程序员气馁和绝望。

    您正在考虑的修复不是修复,它有很大的潜力使情况变得更糟。虽然用户单击异常对话框上的继续按钮是一个值得怀疑的结果,但在全局异常处理程序中吞下异常要糟糕得多。

    是的,为 ThreadException 编写一个替换处理程序。让它在消息框中显示 e.Exception.ToString() 的值,以便用户知道发生了什么。然后发送一封电子邮件或附加到错误日志中,以便知道出了什么问题。然后调用 Environment.FailFast() 这样就不会造成更多的损害。

    对 AppDomain.CurrentDomain.UnhandledException 执行相同的操作。它不会得到太多的锻炼。

    使用反馈来改进您的代码。您会发现需要验证的地方。您可以帮助客户的 IT 人员诊断其 LAN 和设备的故障。您会发现您自己的 try/catch 块可能能够从异常中恢复的极少数情况。

    【讨论】:

    • 一个快速跟进:假设您正在记录(这几乎发生在所有事件中),但它失败了。你做什么工作?记录器是否应该抛出异常(并因此在被全局处理程序捕获时关闭整个应用程序)?您的记录器是否抛出异常(或仅返回错误代码)?我们如何确定日志记录失败了?我们无法记录它。用户应该得到一个消息框吗?啰嗦,是的,但我关心的是所有日志记录和“程序审计”(进入数据库)。这就是为什么我一直养成尝试/捕捉所有事件的习惯。
    • @JustLooking:当你被电源线绊倒并拔掉机器时你会怎么做?您要求某人重新布置电缆,以免再次发生。是的,消息框。
    • 第二个对话框的消息框? (没有关闭的东西)?还是全局异常处理程序中出现的 MessageBox(整个应用程序都崩溃了)? (如果我跑题了,请原谅我,我很好奇你的看法)
    【解决方案2】:

    您可以使用AppDomain.CurrentDomain.UnhandledException 处理程序来拦截主 UI 线程上的错误并在每个对话框中处理它们。来自 MSDN:

    在使用 Windows 的应用程序中 表单中未处理的异常 主应用程序线程导致 Application.ThreadException事件 待提高。如果这个事件是 处理,默认行为是 未处理的异常不会 终止应用程序,尽管 应用程序处于未知状态 状态。在这种情况下, UnhandledException 事件不是 提高。这种行为可以改变 通过使用应用程序配置 文件,或使用 Application.SetUnhandledExceptionMode 将模式更改为的方法 UnhandledExceptionMode.ThrowExceptionThreadException 事件之前 处理程序已连接。这适用 仅适用于主应用程序线程。 引发UnhandledException 事件 对于抛出的未处理异常 其他线程。

    【讨论】:

      【解决方案3】:

      如果您在组合框事件处理程序中处理可能引发异常的事情,您可能需要稍微重新考虑应用程序的设计。

      另一种方法是在将对话框显示给用户之前使用它需要的所有信息来初始化对话框。用户然后进行选择,然后按确定,然后父对话框可以处理对话框中的信息。

      然后可以在父对话框中完成异常处理。

      如果您需要根据用户操作动态更新对话框中的数据,这当然不合适...

      例如

      MyDialog myDialog = new MyDialog();
      myDialog.Init(//data for the user to choose/manipulate);
      if(myDialog.ShowDialog() == DialogResult.OK)
      {
      try{
      ProcessDialogData(myDialog.SomeDataObject);
      }
      catch(/*...*/}
      }
      

      HTH

      【讨论】:

      • 正如我在其他地方提到的,这是一个企业应用程序,因此有程序审核(用户单击此按钮等)并记录了我们的许多事件。这些是我认为可能失败的事情类型。你是对的,SelectedIndexChanged 代码(除了我提到的)很少失败。也许我在我的问题中选择了一个不好的事件。
      【解决方案4】:

      WinForms 应用程序中的全局异常处理是使用两个处理程序完成的:Application.ThreadException 和 AppDomain.CurrentDomain.UnhandledException。 ThreadException 在主应用程序线程中捕获未处理的异常,而 CurrentDomain.UnhandledException 在所有其他线程中捕获未处理的异常。全局异常处理可用于以下目的:显示用户友好的错误消息、记录堆栈跟踪和其他有用信息、清理、向开发人员发送错误报告。捕获未处理的异常后,应终止应用程序。您可能想重新启动它,但至少在非平凡的应用程序中,无法纠正错误并继续。

      全局异常处理不能替代局部异常处理,仍然应该使用。本地异常处理程序不应该使用 catch Exception,因为这有效地隐藏了编程错误。在每种情况下都必须只捕获预期的异常。任何意外的异常都会使程序崩溃。

      【讨论】:

        【解决方案5】:

        听起来你想要方面。 PostSharp 可以帮到你。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-10-13
          • 2019-08-24
          • 2016-05-17
          • 2013-02-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多