【问题标题】:Why are try-catch in main() bad?为什么 main() 中的 try-catch 不好?
【发布时间】:2014-11-25 23:10:49
【问题描述】:

有人可以向我解释为什么认为在 main() 方法中使用 try-catch 来捕获任何未处理的异常是不合适的吗?

[STAThread]
static void Main()
{
    try
    {
        Application.Run(new Form1());
    }
    catch (Exception e)
    {
         MessageBox.Show("General error: " + e.ToString());
    }
}

我知道这是不好的做法,但不知道为什么。

【问题讨论】:

  • 您可能希望向最终用户展示比这更多的信息,并为支持目的记录异常详细信息,否则我建议将其作为最佳实践而不是不合适的!
  • 好问题,实际上。我认为这里的答案有点“微妙”。仅仅将这种方法称为“不良做法”并不公平。

标签: c# exception


【解决方案1】:

我不认为它一定是不好的做法。然而,有一些警告......

我相信,无论谁称这种“不良做法”,都是为了强化这样一种观念,即您应该在最接近它们发生的地方捕获异常(即尽可能/适当地在调用堆栈的顶部)。一揽子异常处理程序通常不是一个好主意,因为它大大减少了您可用的控制流。粗粒度异常处理非常重要不是程序稳定性的合理解决方案。不幸的是,许多初学者开发人员认为是这样,并采用这种笼统的 try-catch 语句这样的方法。

这么说,如果您在程序的其余部分中正确地使用了异常处理(以细粒度和特定于任务的方式),并在那里相应地处理了错误(而不是仅仅显示一个通用的错误框),然后一个通用的 try-catch Main 方法中的所有异常可能是一个有用的东西。这里需要注意的一点是,如果您可重现在此Main try-catch 中捕获了错误,那么您要么有错误,要么您的本地化异常处理有问题。

这个 try-catch 与 Main 的主要用途纯粹是为了防止你的程序在非常不寻常的情况下崩溃,并且应该只显示一个(模糊的)用户友好的“致命错误”消息给用户,以及可能在某处记录错误和/或提交错误报告。所以总结一下:这个方法确实有它的用处,但必须非常小心,不要因为错误的原因。

【讨论】:

  • +1 将其称为“不良做法”可能与实际上是不良做法不同。
  • 我同意诺多林的观点。如果您对应用程序可以从中恢复的异常进行了细粒度处理,那么在此处出现的异常要么是错误,要么是不可恢复的(有时当发生不好的事情时,您无能为力)。您将使用此通用异常处理程序来尝试记录一些信息并向用户显示消息(注意:不保证您可以记录严重异常——您可能内存不足,或者您可能无法访问磁盘等)
  • @JMarsch:是的,没错。即使这样,一些非常严重的错误也不会被捕获,尽管它对于在测试期间没有被捕获的错误(最好是报告)或只是古怪的罕见事件的故障保护很有用。
  • 我认为区分在 main 中使用 try-catch 来捕获您实际上想要全局处理的特定错误和使用 try-catch 在您的应用程序只是为了防止它崩溃。如果你想防止崩溃,你应该写好你的应用程序,这样它就不会崩溃。
【解决方案2】:

好吧,这个方法只会捕获在你的主线程中抛出的异常。如果您同时使用 Application.ThreadExceptionAppDomian.UnhandledException 事件,那么您将能够捕获并记录所有异常。

【讨论】:

  • Application.ThreadException 仅捕获 GUI 线程上的异常。
【解决方案3】:

我根本不认为这是一种不好的做法。

让您的程序因未处理的异常错误而崩溃不会给您的最终用户灌输任何信心。

也许其他人可以提供相反的观点。

更新: 显然,您需要对例外做一些有用的事情。

  1. 记录下来
  2. 向用户显示一个对话框,说明应用程序退出的原因(以纯文本形式,而不是堆栈跟踪)
  3. 在您的应用程序上下文中有意义的其他内容。

【讨论】:

  • 新开发人员常犯的一个错误是捕获所有异常。这通常是不受欢迎的,因为它有可能掩盖本来可以解决的错误。如果您无法从异常处理程序中的错误中恢复,最好让应用程序崩溃。这些类型的错误更有可能被最终用户报告。阅读此链接了解更多信息:blogs.msdn.com/fxcop/archive/2006/06/14/631923.aspx
  • 我部分同意在整个代码中捕获所有异常是不好的。在您的 main 中捕获未处理的异常并在退出之前对其进行处理不应被视为坏事。这完全取决于你想做什么。您链接到的文章通常是正确的,但您应该注意,它没有处理这个问题中提出的具体案例,它在 main 中捕获了一个异常。
【解决方案4】:

我认为这本身并不是一个坏习惯。我认为不好的做法是,如果这是您的应用程序中唯一的 try/catch 块。

【讨论】:

    【解决方案5】:

    在古代,在 C++ 中放置一个 try/catch 会导致相当严重的性能损失,而在 main 周围放置一个将意味着为所有内容存储额外的堆栈信息,这同样不利于性能。

    现在计算机速度更快,程序员不那么沉迷于性能,并且运行时构建得更好,所以它不再那么糟糕了(但你仍然可能会为此付出更多,因为多年来没有对它的效果进行基准测试)。所以这是古老的民间传说,比如逆向迭代(编译器实际上现在已经为你修复了迭代)。在 C# 中它非常好,但对于 10 年前的人来说,它看起来很可疑。

    【讨论】:

      【解决方案6】:

      任何到达Main() 的异常都可能是致命的。

      如果这很容易,它应该被更高的处理。如果它超出了您的控制范围,例如 OutOfMemoryException,那么程序应该崩溃。

      崩溃的 Windows 应用程序有一个标准的方法,它们会触发Windows Error Reporting 对话框。 (你可能以前见过)。发生这种情况时,您可以注册以接收崩溃数据。

      【讨论】:

        【解决方案7】:

        我不确定我是否认为这是一种不好的做法。您要做的是确保程序崩溃时的异常和当前状态最终掌握在开发人员手中,最好记录日期、时间和使用它的用户。基本上 - 您要确保您的团队拥有调试问题所需的所有信息,无论用户是否向他们询问崩溃。请记住,事实上,许多用户在遇到崩溃时不会联系支持人员。

        这里的不好的做法是捕获异常,显示一个简单的“错误”对话框,然后关闭应用程序。在这种情况下,该异常的状态将永远丢失。

        【讨论】:

          【解决方案8】:

          从调试的角度来看,这会使生活变得更加困难,因为它使每个异常都成为用户处理的异常。这会改变调试器的行为,除非您中断未处理的异常,否则可能会出现其他问题。

          话虽如此,我认为在发布时这是一个很好的做法。此外,我还建议收听​​AppDomain.UnhandledExceptionApplication.ThreadException 事件。这将让您捕获更多异常(例如一些不会在上面的“全局”处理程序中捕获的系统异常)。

          这允许您记录错误并为用户提供良好、清晰的消息。

          【讨论】:

            【解决方案9】:

            改成这样就可以了

            catch(Exception ex)
            {
                YourLoggingSystem.LogException(ex);
            }
            

            当然,永远不要点击这一行,因为您的代码中会有其他异常处理程序来捕获更多上下文的内容。

            【讨论】:

              【解决方案10】:

              顶级异常处理非常重要,但我建议使用:

              Application.ThreadException += new ThreadExceptionEventHandler(YourExceptionHandlingMethod); 
              

              但是,这只会捕获 GUI 线程上的异常(很像您的 try..catch 块) - 您应该为每个新线程使用类似的代码来处理来自它们的任何意外异常。

              更多信息here

              【讨论】:

                【解决方案11】:

                你有一个包罗万象的异常,它将捕获所有内容。因此,如果您的代码中有任何未处理的异常,您将永远看不到它们。

                从积极的方面来说,您的应用程序永远不会崩溃!

                如果这是必需的行为,那么您需要有足够的日志记录和报告,以让用户和您作为开发人员都知道发生了什么,并尽可能优雅地恢复或退出。

                【讨论】:

                • 不能万能的做一些有用的例外,例如记录它,显示错误对话框,发出警报等。然后程序可以正常退出。这似乎比崩溃要好
                【解决方案12】:

                我并不是说这是不好的做法,但我唯一会做的不同是使用内置事件:

                        Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
                
                        static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
                        {
                            MessageBox.Show(e.Exception.Message); //or do whatever...
                        }
                

                【讨论】:

                  【解决方案13】:

                  我认为这是不鼓励的,因为异常只被捕获一次,并且可能有多个事情,比如后台线程,需要知道进程是否抛出。

                  这是一个 WinForms 应用程序吗?每当线程抛出未处理的异常时,Forms.Application.Run 就会引发事件。 MSDN 文档试图解释这一点,但他们显示的处理程序不起作用!从 WinForms UE 站点阅读updated example

                  【讨论】:

                    【解决方案14】:

                    如果你的程序试图继续运行,尽管下面有一个万能的捕捉谁知道什么样的违反假设,那么它就处于远程利用安全漏洞(缓冲区溢出、堆损坏)之类的危险区域。将其置于循环中并继续运行是软件质量的一大危险信号。

                    尽快离开是最好的,例如退出(1)。记录和退出是好的,但风险稍高。

                    不相信我? Mitre Common Weakness Enumeration (CWE) agrees。与之密切相关的是它的advice against catching NULL pointer dereferencing这种方式。

                    【讨论】:

                      猜你喜欢
                      • 2010-09-12
                      • 2017-07-03
                      • 1970-01-01
                      • 2011-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-05-27
                      • 2010-10-16
                      • 1970-01-01
                      相关资源
                      最近更新 更多