转载:http://blog.csdn.net/Donjuan/archive/2008/12/05/3454650.aspx
1.1.1. 在程序抛出异常的时候中断程序的执行
.NET对异常的良好支持,让我们摆脱了以前在C和C++程序开发过程中,那种不停的检查返回值的编程模式,使我们的程序变的更为简洁。异常不仅方便我们更高效的编写代码,同时也提供了一个好用的调试技巧—当然需要调试器的良好支持。在程序开发的过程中,可能会经常碰到这种情况,程序在运行很长时间后,突然抛出一个异常以后,就中断执行了。这种错误(Bug)在平常执行时,一般很难重现,除了异常里面的堆栈信息以外,你几乎没有任何其他信息让你知道程序内部到底发生了什么。这是你就可以使用CLR Debugger里面的异常断点。
在CLR Debugger里面,点击菜单栏里的“Debug”菜单里的“Exceptions…”菜单项,打开“Exceptions…”对话框设置异常断点:
图 1-6 CLR Debugger 异常断点设置对话框
在“Exceptions”对话框里面,勾上相应异常类型名旁边的复选框就可以设置,在这些异常抛出的时候,“Break when an exception is”列表中有两列复选框:“Thrown”和“User-unhandled”列,这两列的区别是:
l 当“User-unhandled”一列的复选框被选中以后,调试器只在程序中抛出指定的异常以后,并且程序中没有捕捉这个异常,调试器才中断程序的执行。而程序中有try…catch块捕捉该异常时,调试器会让程序继续执行,并忽略这个异常。
l 当“Thrown”一列的复选框被选中以后,则无论程序是否有try…catch块捕捉该异常,只要扔出了指定的异常,则调试器都会中断程序的执行。
小技巧: 选择命名空间旁边的复选框,会将该命名空间中定义的所有异常选中。 |
“Exceptions”对话框中只列出了CLR当中定义的异常,如果你希望调试器在自定义的异常被抛出的时候,也能中断程序的执行,则你可以在“Exceptions”对话框中将自定义异常添加进来。点击“Add”按钮添加自定义异常,注意,你必须提供自定义异常的完整类名—即命名空间加上类名。下面的示例代码演示了这一过程:
using System; using System.IO;
namespace TestNamespace { public class TestException : Exception { }
public class ExceptionBreakpoint { static void Main() { // 在“New Exception”中添加TestNamespace.TestException异常 // 并且在“Throw”中勾上“TestNamespace.TestException”复选框 // 下面的语句才会导致程序中断,并且跳入到调试器当中 try { for (int i = 0; i < 10; ++i) { if (i == 5) throw new TestException();
Console.WriteLine(i); } } catch (Exception e) { Console.WriteLine(e.Message); }
// 无论“Exception”对话框中的“Throw”或“User-unhandled”是否 // 被选中,下面的语句总能导致程序中断,并且跳入到调试器中 using (StreamReader reader = new StreamReader( "notexists.txt")) Console.WriteLine(reader.ReadToEnd()); } } } |
表 1-2 异常断点的演示代码
在上面的例子当中,细心的读者会发现,当调试器捕获在try…catch块中抛出的异常时,高亮显示的当前代码并不是包含throw语句的那一行,而是throw下面的一行,而捕捉到using语句那一行抛出的异常后,高亮显示的当前代码行正是扔出异常的那一行。出现代码行偏差(姑且叫做代码行偏差吧)的原因会在后面的文章讲解调试器的工作原理时讲到。
“Exceptions”对话框中还有一种异常类型:“Managed Debugging Assistants”是CLR 2.0新增加的特性,我们将会在讲解使用Visual Studio进行调试的时候讲到它。