【问题标题】:C# Try-Catch & Exception handlingC# Try-Catch 和异常处理
【发布时间】:2012-05-04 16:21:33
【问题描述】:

在当前项目的各个部分代码中,都有不断的使用

try
{
    //code
}
catch(Exception e)
{
    //display nicely formatted error message
}

我的问题是,假设代码处理所有已知/假定的错误(例如检查对象是否为空等),是否应该在以应用程序全局格式显示错误的代码周围放置一个 try-catch?

或者让错误引发异常并让应用程序自然崩溃是更好的做法?

谢谢

【问题讨论】:

  • 应用程序不应该“自然崩溃”。即使您的汽车有保险杠 - 我建议您改用刹车。
  • 我同意@zerkms,应用程序永远不应该自然崩溃...查看this有关C#异常处理的基本文档...
  • 您是否正在开发一个不会自然崩溃的日志记录模块?
  • 过去在 StackOverflow 上已经无休止地介绍了这一点,您应该在发布问题之前进行搜索。经验法则:只有当你要在那里采取行动或为它增加价值(即转换它)时才捕获异常。我会看看我是否能找出一个合理的先前问题关于这个。

标签: c# asp.net .net exception-handling try-catch


【解决方案1】:

这里有两种截然不同的思想流派,两者的支持者常常对任何人都可能相信另一种方式感到震惊和惊讶。最后,这是一个偏好问题,以及您的目标用户群期望和/或愿意接受什么,更重要的是,您对应用程序功能的了解程度如何。 (你的问题暗示你没有编写所有涉及的代码,所以最后一点可能比你想象的要棘手。)

首先,我假设您在这里谈论的是最终用户应用程序,而不是类库。几乎没有充分的理由在您没有明确处理的类库中捕获异常。你总是把决定权留给来电者。

但是,在最终用户应用程序中,最终决定权在您手中,因为调用链上没有任何人。在这种情况下,您有两个广泛的选择:

快速失败

有些人认为,当遇到错误时,最好让应用程序在物理上尽快崩溃。虽然这似乎有点适得其反,但请记住遇到未处理的异常意味着什么:您实际上不知道出了什么问题;即使是像您的错误日志代码这样简单的事情也可能以您意想不到的方式运行。

如果您在调用站点正确处理异常,则只有在发生非常非常糟糕的事情时才会收到这种“意外”错误:内存不足、堆栈损坏等。那种事情你不能无论如何,真的要从中恢复过来,所以尝试没有意义。

优雅地失败

其他人认为,您应该尽可能隔离错误,以防止您的用户丢失信息。如果您的应用程序足够模块化,那么一个区域的完全故障可能对另一区域的数据完全没有影响。

这是一个风险更大的提议:您必须非常小心,不要让自己处于不一致的状态。它要求您确定地知道,失败代码中的任何内容都不会从代码的其他部分更改状态。

结论

像往常一样,“正确”的答案可能在中间的某个地方。一般来说,在不一致的状态下运行的风险几乎总是让应用程序死掉的好理由。当然,您可能仍希望在此处进行某种程度的日志记录,您可以通过多种方式完成。我的偏好不是为此使用异常,而是挂钩到顶级的各种“未处理异常”事件,例如thisthisthis。在这些事件处理程序中记录您的异常,可能会显示一个“更友好”的错误对话框,然后让您的应用程序关闭。例如,我们在我们生成的 WPF 应用程序中放置了类似的内容:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             DispatcherUnhandledException="UnhandledException">


private void UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    logger.FatalException("OH NOES! Bad stuff happened, bailing out.", e.Exception);
    MessageBox.Show("holy cow something bad happened:\r\n" + e.Exception.Message);
}

但是,在某些地方捕获所有异常可能是安全的;这主要限于无论如何都会丢弃所有数据的地方。例如,打印或导出已完成操作的结果可能会“安全地”失败,而不会对应用程序的其余部分产生不良影响。您可能不希望您的应用程序因为用户没有打印机而崩溃。 (我什至遇到过一个第三方打印库的案例,它在错误中明确抛出System.Exception...)但是您必须非常小心,您知道自己在做什么,并且您的数据是真正隔离的来自系统的其余部分。

【讨论】:

    【解决方案2】:
    class PerformTask
    {
        public void ConnectToDB(requiredParams)
        {
    
    
        }
    }
    
    class PerformTaskConsumer
    {
       public void SomeMethod()
       {
          try
          {
             PerformTask obj = new PerformTask();
          }
    
          catch(RelaventExceptions)
          {
    
          }
       } 
    
    
    }
    

    如果可以,请避免异常。如果发生异常,将它留给调用者他想对异常做什么。调用者可能决定显示有关异常的格式正确的消息,或者决定崩溃或其他任何情况。

    Eric Lippert 有一篇关于异常的好文章。 here

    【讨论】:

      【解决方案3】:

      提示您知道在哪里放置 try ... catch 块。

      放一个全局异常处理程序,捕获所有未处理的异常。记录堆栈跟踪和异常,向最终用户道歉并关闭程序(永远不要尝试在此处继续)。

      让应用程序运行,当它崩溃时,检查原因并正确处理异常,尽可能使用最具体的异常。

      记住: 异常是针对异常,而不是针对正常的程序行为。

      【讨论】:

        【解决方案4】:

        像这样捕获所有异常不是一个好习惯,除了在应用程序的顶层,你会显示一个很好的错误消息。如果做得好,它可以保留最终用户对您的程序的看法,即略有错误,但总体上是合理的。相比之下,让您的应用程序崩溃是让您的最终用户相信您的应用程序已经无法修复的最简单方法。

        就异常预防而言,主要有两种异常——编程错误(空指针、类转换、超出范围等)和环境错误(找不到文件、网络路径不可用、用户输入错误)等)你可以而且应该避免第一种,并准备好处理第二种。

        【讨论】:

        • 在某些情况下,这些异常之间的界限可能很模糊,尤其是在从磁盘读取文件时。如果应用程序 Fnord 被要求打开一个以包含 Fnord 特定 GUID 的标题开头的文件,并且如果该文件不符合某些标准,将无法对该文件执行任何有用的操作,则假定该文件的代码将满足标准可能比验证文件各个方面的代码更具可读性,如果文件无效,可能会引发异常。
        猜你喜欢
        • 2011-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-10
        • 2019-01-07
        • 1970-01-01
        相关资源
        最近更新 更多