我发现了几个C#应用程序崩溃,以响应obj = null
或obj.member = null
等错误情况。很多时候,来自3rdPartyApp界面的obj。
并导致3rdPartyApp和MyCsApp崩溃。
如何在所有可能的区域添加异常处理,以便我的应用程序能够在这些灾难性的情况下生存?向所有地方添加try-catch并从这种情况中恢复是一项挑战。
我怎样才能以现实,可靠和防弹的方式实现这一目标?
[更新:工业自动化控制]
结构:
GUI(asp.net,c ++) - RuntimeApp(C ++) - MyCsApp(cs) - 3rdPartyApp(Cs)
正常程序:
异常情况:
我最好处理所有异常情况。 最重要的是,我必须考虑如何从这些情况中恢复过来。
答案 0 :(得分:19)
你不希望在任何地方捕获每个例外。
您希望防止异常“泄漏”应用程序的较低层,直到它们可以杀死应用程序或破坏它。
但是,防止腐败需要的不仅仅是捕获异常。您将不得不确保应用程序始终可以安全地在可能引发异常的每个位置进行中断。这可能意味着您需要清理复杂的操作。例如:
ComplexBuilder cb = new ComplexBuilder();
try
{
cb.AddOperation(...); // Once building starts,
cb.AddOperation(...); // it's not safe to use cb
cb.AddOperation(...);
}
catch (SpecificException ex)
{
cb.Cleanup(); // until it's cleaned up
}
// Now safe to access cb, whether or not an exception was thrown
我最近遇到了一个态度相似的应用程序。有一个被认为是“重要”的应用程序。当那个“重要”的事情发生时,还有其他的东西应该发生,但被认为是“不重要”。这个想法是,如果“不重要”部分有例外,那么“重要”部分就必须继续。
由于某种原因,尝试读取资源失败了。返回null而不是字符串资源。这导致ArgumentNullException
来电String.Format
。这导致异常被刚刚继续的代码捕获。
但是在第一个异常和最后一个异常之间,已经分配了一个对象,并且已经设置了对该对象的引用。但由于例外,设置引用从未发生过。结果是我看到了NullReferenceException
,四个堆栈级别,以及两个.csproj文件远离实际问题发生的位置。
因此,当您谈到捕获异常以便程序可以继续时,您需要记住,通过捕获所有这些异常,程序的控制流程会发生巨大变化。实际上,它可能会发生很大变化,以至于您无法再确定程序是否可以继续执行。
答案 1 :(得分:7)
许多开发人员都没有这样做。当您的异常catch-all被击中时,您的应用程序已经崩溃。发生了意外的事情,这意味着您的代码没有预料到它,并且事情很可能处于不确定的状态(即,您无法确定究竟有多少违规函数在生成异常的那一点,你不知道写出了多少数据,在硬件中设置了什么位等等。继续下去是否安全?你应该试着保存用户的数据吗?谁知道!
当您达到这个高级别的全部内容时,您还没有阻止您的应用崩溃。那时你只是决定如何处理崩溃。您可以提供与标准不同的消息:
此应用程序执行了非法操作
...但是你的自定义信息会更好吗?
我们在没有任何警告的情况下关闭 对于计划外维护,但休息 保证它与无关 这个优秀软件的缺陷
...
答案 2 :(得分:4)
你当然不应该在任何地方添加try catch
你只需要一个顶级捕获所有异常。如果这是一个GUI应用程序,那么只需显示一个漂亮的对话框,其中有一个按钮,说“请报告给支持”(它可以写出一个堆栈跟踪快照到屏幕或文件)
如果你很幸运,那么该应用程序可以继续(幸运的是因为你无法知道你是否真的被困住了)
请注意,您也可以这样做
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Forms.Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
但是这并没有阻止它崩溃,只是让你捕获失败
答案 3 :(得分:3)
首先治愈疾病是有道理的,找出导致崩溃的原因,确保代码因obj = null
或类似问题而崩溃 - 使用异常处理和吞咽所有异常只是掩盖了问题。 ..这就是它不用的东西!听起来有很多代码气味会引发崩溃 - 保护你的应用程序免于崩溃不是处理它的正确方法,只会让事情变得更糟......
好的,你可以按照约翰·桑德斯和pm100的建议来做到这一点......但要处理它的方式是什么是根本原因,不要把它当作一个“魔法银弹”,在一天结束时,需要彻底调试与第三方应用程序交互的代码......
例如
object foo = null; bar baz; // .... // foo is now set by thirdparty app if (foo != null && foo is bar) baz = (bar)foo as bar; if (baz != null){ // Continue on, baz is a legitimate instance of type 'bar' }else{ // Handle it gracefully or throw a *user defined exception* }
注意'as'用于检查'foo'是否为'bar'实例的正确类型 - 现在与此进行比较,这是典型的代码味道......
object foo = null; bar baz; // foo is now set by thirdparty app - ARE YOU REALLY SURE ITS NON-NULL? // IS IT REALLY OF TYPE 'BAR'? baz = foo; // CRASH! BANG! WALLOP! KERRUNCH!
答案 4 :(得分:0)
避免此类例外的一种方法是使用the null object pattern。
因此,您不必拥有Account account = null;
Account account = Account.SpecialNullAccount;
,而是在您的帐户类中,您可以将SpecialNullAccount
定义为静态Account
对象。现在,如果您尝试在某些无关紧要的代码中使用account.Name
(例如,记录代码),则不会得到异常,而是获得一个值,例如在静态空对象实例上定义“NO NAME”。
当然,重要的是这样的对象在任何重要的情况下都会抛出异常,例如.PayInvoice()或objectContext.SaveChanges(),所以要采取措施确保发生这种情况。
答案 5 :(得分:0)
AppDomain.UnHandledException和/或AppDomain.ThreadException是您可以订阅以捕获未处理的异常的事件。我不认为你可以用这个来继续执行它停止的地方(这可能不是一个好主意)。可以使用它们吞下错误信息或记录它等等。
然而,这是否是一个好主意取决于你!
答案 6 :(得分:0)
如果是Winforms应用程序,则为Main方法中的以下事件添加事件处理程序,如此
Application.ThreadException += Application_ThreadException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
然后,您可以显示消息框或其他通知,隐藏异常等并继续工作而不会使应用程序崩溃。