作为程序员,我想很多人都应该有处理异常的经历。 而且我相信很多人也写过像catch(Exception e){//blabla}这样一次性捕获并处理所有未知异常的代码。 无论是为了敷衍客户,还是为了让程序继续运行以避免不好的用户体验,在微软看来,这种做法都是错误的,尤其是当你的程序作为插件托管在VS等其他程序中时,Offcie,这样的话,对于一些严重的异常比如访问违规,我们应该让程序结束而不是继续运行。 然而,很多时候,我们并不知道哪些异常是严重的,哪些异常可以让程序继续运行,因为在.NET 4.0之前,CLR会忠实地将所有大大小小的异常抛给程序员。 处理。 不过这个问题在4.0之后就会得到很好的解决。 因为一些可能导致进程崩溃的严重异常的处理,以后都会由CLR统一处理,不再留给我们可怜的程序员。 下面我就对这个不寻常的处理做一些简单的介绍。
为什么需要损坏状态异常
例外情况可能很大也可能很小。 小的问题,例如空字符串,通常是用户输入问题。 它们不会导致整个程序或系统中的相关进程崩溃。 较大的异常,例如访问冲突异常,可能是由您的程序引起的。 当做一些可能导致操作系统崩溃的事情时,这种异常一般比较严重。 一般情况下,如果出现这种异常,程序应该做的就是结束当前进程,然后如实向用户报告你很笨,并提示他。 重新启动程序。 然而在.NET 4.0之前,CLR认为程序员不会创建catch(Exception e){return;}这样的不负责任的代码,所以它没有优先级,只要是异常,它就会扔掉,那里不仅是托管代码异常,还有一些.NET程序员不容易理解的COM和WIN32异常。 CLR认为程序员在捕获异常时只会处理他们清楚知道的异常。 但是,很多时候,作为开发者,由于上面有老板,下面有客户,我们确实很难做人。 想想看,如果老板总是在倾听、倾听。 如果客户抱怨说,他只是按了两次按钮游戏动态,程序就报错并结束了,他还能给你加薪吗? 虽然很多时候我们知道我们的代码不会出现问题,但是我们很难保证有合适的时间、地点和人员。 为了给老板和客户一个交代,很多人此时会选择捕获所有异常,然后记录异常。 信息,然后程序继续大力运行。
似乎其中一些是完美的。 客户不会再像以前那样频繁地抱怨程序宕机了,老板也会高兴的。 但有人不高兴。 当然,小的未知异常不会造成大问题,但是一些可能导致程序甚至操作系统崩溃的异常如果不中断程序的话可能会产生很大的影响。 这时候客户可能不会抱怨你,但是他会抱怨微软操作系统不好,整天蓝屏,或者抱怨微软Office或者IE太糟糕。 他刚装了一个插件,整个Outlook就坏了。 错误消息崩溃了。 你省了麻烦,但是微软会受到指责3D角色,他们不知道这个替罪羊到底是怎么回事。
当然上面是玩笑,但无论怎样,从程序安全性和稳定性的角度来看,捕获(Exception e)确实不是一个好的编程习惯。 然而,交易已经完成。 既然无法阻止程序员偷懒,那么微软只能采取一些补救措施。 这就是 CLR 新的异常处理机制的作用。
CLR 如何处理损坏状态异常
从4.0开始,CLR不会主动为你抛出所有异常。 对于那些它认为危险、可能导致进程崩溃的异常,它会将其标记为Corrupted StateException并自行处理,而不是抛给程序员,比如AccessViolationException,继承自SystemException的异常,都会被处理作为损坏的 StateException。 但是,这里需要注意的是,仅仅异常类型是潜在危险的异常是不够的。 CLR 还将确定引发异常的所有者。 如果它发现操作系统抛出访问冲突,它将认为这是状态崩溃。 异常,但如果异常是用户代码抛出的,CLR不会对其进行特殊处理,仍然会像以前一样正常抛出。
下面我们通过具体的例子来演示.NET 4.0中CLR对Corrupted State Exception的处理。
首先,我们想办法创建这样一个严重的异常,然后使用try和catch来看看CLR是如何处理这段代码的。
01不安全静态voidT2()
02{
03尝试
04{
05int*pd=stackallocint[1];
06pd[1911]=2;
07Console.WriteLine(pd[1111]);
08}
09catch(异常)
10{
11Console.WriteLine(e.ToString());
12}
13}
在这段代码中,即使用户代码捕获了所有常见异常,程序仍然会终止并抛出 AccessViolationException 异常信息。 这是因为 AccessViolationException 是一个 CorruptedState 异常,并且是由系统抛出的。 所以这里CLR不会把这个异常交给用户处理,而是会主动报告错误,中断进程,然后退出程序。
我们看另一段代码:
01staticvoidT3()
02{
03尝试
04{
05抛出newSystem.AccessViolationException();
06}
07catch(异常)
08{
09Console.WriteLine(e.ToString());
10}
11}
虽然这里的代码仍然会抛出AccessViolationException,但由于它是在用户代码中抛出的,所以CLR不会拦截它并自行处理,但仍然将处理责任留给了用户。 因为这里的异常不是由系统生成的,所以它假设用户知道该异常并且可以对此负责。 因此,该方法执行后程序不会被中断,而是会继续执行。
此外,CLR 只会自行处理这种危险的损坏状态异常。 对于其他不会引起严重问题的异常,它仍然会像往常一样将它们抛给程序员。
如何继续捕获损坏状态异常
那么CLR包含了这种异常处理,是不是意味着以后我们程序员就别无选择,只能老老实实的向用户报告我们的产品不行了,然后让老板解雇我们呢? 对于.NET 4.0之前发布的、漏洞百出的产品,我们该如何处理?
尽管微软不再相信程序员是负责人c#异常处理机制 unity,但它仍然如此。 虽然.NET 4.0之后CLR会默认处理这些异常,但程序员不再需要担心这些危险的异常。 但你仍然可以继续用以前与老板打交道的方式。 并且微软还提供了两种方法。
首先,对于以前的程序,微软提供了两种选择:
1、如果你想在.NET Framework 4.0下编译旧代码但又不想改变代码,你可以在你的程序的配置文件中添加一个新的节点:legacyCorruptedStateExceptionsPolicy=true,这使得你的代码仍然可以继续按照之前处理异常的方式运行。
2. 如果不想做任何更改,只需在.NET Framework 4.0下运行之前编译的程序即可,无需任何更改。 CLR 将确保所有异常仍以与以前相同的方式处理。
其次,对于那些使用.NET Framework 4.0但想要自行处理导致程序状态崩溃的异常的人,微软也提供了选项。 他们在.NET 4.0中添加了一个新的命名空间:System.Runtime.ExceptionServices,其中有一个名为HandleProcessCorruptedStateExceptionsAttribute的要素类。 你只需要在相应的方法中添加这个属性,CLR就会把所有的异常处理交给你,就像以前一样。 例如
01//该程序作为自动化测试系统的一部分运行,以便您需要
02//防止正常的UnhandledException行为(Watsondialog)。
03//相反c#异常处理机制 unity,打印出任何异常并退出并带有错误代码。
04[处理损坏状态异常]
05publicstaticintMain()
06{
07尝试
08{
09//捕获程序中泄漏的任何异常
10调用主程序循环();
11}
12catch(Exceptione)//我们可以在这里捕获任何东西
13{
14 //我们捕获的异常可能是程序错误
15//或者更严重的事情。无论如何,我们知道
16//有些东西不对劲,我们就输出异常
17 //andexitwithanerror.Wewon'ttrytodoanyworkwhen
18//程序或进程处于未知状态!
19System.Console.WriteLine(e.Message);
20返回1;
21}
22返回0;
23}
1 当然,需要注意的是,该功能只能应用于方法。
Winform程序中捕获未处理异常的代码:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Application.Run(new frmMain());
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
LogUtil.Instance.Fatal("AppDomain中遇到未处理异常:" + e.ExceptionObject.ToString());
}
static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
LogUtil.Instance.Fatal("Application中遇到未处理异常:" + e.Exception.Message + "\r\n" + e.Exception.StackTrace);
}
总结
异常处理常常是程序员心中的一个烦恼。 尽管微软认为它必须对允许程序员滥用异常捕获负责,然后添加了这种新的异常处理机制,但在他们看来,catch(Exception e)的行为仍然是不对的。 他们认为异常的发生表明当前程序状态有问题,程序员应该意识到这些错误状态所带来的后果,因此程序员应该捕获特定的异常并正确处理,而不是偷懒或采取简单的办法。 处理所有异常。