为什么main()中的try-catch不好?

时间:2009-05-18 15:12:50

标签: c# exception

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

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

我理解这是不好的做法,但不确定原因。

15 个答案:

答案 0 :(得分:28)

我不认为它的必然不好的做法。但是有一些警告......

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

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

这个带有Main的try-catch的主要用途纯粹是为了防止你的程序在非常特殊的情况下崩溃,并且几乎不应该只显示(模糊地)用户友好的“致命错误”向用户发送消息,以及可能在某处记录错误和/或提交错误报告。总而言之:这个方法确实有它的用途,但必须非常谨慎地完成,而不是出于错误的原因。

答案 1 :(得分:11)

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

答案 2 :(得分:7)

我根本看不出那种糟糕的做法。

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

也许其他人可以提供反制视图。

更新: 显然你需要做一些有用的例外。

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

答案 3 :(得分:5)

我认为这本身并不是一种坏习惯。我认为不好的做法是,如果那是你申请中唯一的try / catch块。

答案 4 :(得分:5)

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

如果它很容易,它本应处理得更高。如果它是您无法控制的,例如OutOfMemoryException,则程序应该崩溃。

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

答案 5 :(得分:5)

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

现在计算机速度更快,程序员对性能不那么沉迷,而且运行时间更好,所以它不再那么糟糕了(但是你可能还需要多付一些钱,多年来没有对它的影响进行基准测试)。所以这是一个古老的民间传说,就像反复对谷物一样(编译器实际上现在为你修复了迭代)。在C#中它完全没问题,但是对于10年前的某个人来说它看起来很不错。

答案 6 :(得分:3)

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

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

答案 7 :(得分:1)

你有一个全能的例外,它会捕获一切。因此,如果您的代码中有任何未处理的异常,您将永远不会看到它们。

从积极的方面来说,你的申请永远不会崩溃!

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

答案 8 :(得分:1)

从调试的角度来看,这可能会使生活变得更加困难,因为它会使用户处理异常的每个异常。这会改变调试器的行为,除非你打破未处理的异常,这可能还有其他问题。

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

这使您可以记录错误并为用户提供良好,干净的信息。

答案 9 :(得分:1)

将其更改为此并且没关系

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

当然,这行不应该被击中,因为你的代码中会有其他异常处理程序捕获更多上下文。

答案 10 :(得分:1)

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

Application.ThreadException += new ThreadExceptionEventHandler(YourExceptionHandlingMethod); 

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

有关它的更多信息here

答案 11 :(得分:0)

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

        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...
        }

答案 12 :(得分:0)

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

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

答案 13 :(得分:0)

如果你的程序试图继续运行,尽管在下面发现了什么样的违反假设的问题,那就是远程利用安全漏洞(缓冲区溢出,堆损坏)这样的危险区域。将其置于循环中并继续运行是软件质量的一个重要标志。

尽可能快地离开是最好的,例如出口(1)。记录和退出虽然风险稍大,但仍然很好。

不相信我? Mitre Common Weakness Enumeration (CWE) agreesadvice against catching NULL pointer dereferencing。与此密切相关的是{{3}}。

答案 14 :(得分:0)

我可以做一个冗长的解释,但你最好只阅读FxCop team blog response