C#异常处理的方式
c#中所有可以被抛出的异常都是直接或间接继承自System.Exception类
支持的捕获异常的语句块如下:
try … catch
try … catch … finally
try… finally
c#代码块中生成异常堆栈信息的时机不是在throw语句执行的地方,而是在第一次捕获的地方
以上三种方式中 try ... finally一定不会影响堆栈信息
可能会影响的地方主要集中在catch块中
catch子句声明方式又有以下几种
catch{}
catch(Exception){}
catch(Exception ex){}
这三种写法从捕获异常的能力上来说基本上是等效
第三种方式只是让编写代码的人可以使用异常参数,如果不使用异常参数的话可以用前面两种,第二种只是更加明确的指出了捕获的是Exception或者是从Exception继承的异常
catch块内可以再次抛出异常,抛出时可以支持下面几种方式
throw;
throw ex; // ex来自于catch(Exception ex)
throw new SomeException();
后面两种情况是一样的, stack trace认为你catch到的异常已经被处理了,只不过处理过程中又抛出新的异常,这时候stack trace就把throw 后的那个异常实例当作错误根源了。之前的堆栈信息全部会清除掉
第一种方式网上和MSDN上都说可以保留堆栈信息。经测试发现不全是如此:
在Debug版本下,生成的代码在执行没有任何优化,直接使用throw;这种方式的确是能保留堆栈信息。这个时候的堆栈信息就好像是在try块内部异常产生的地方就记录好了一样。
在Release下,输出的信息中只包含了从捕获位置开始向上的堆栈信息。即从异常源发生位置到第一次catch块捕获发生之间的堆栈信息都不存在了
总结如下:
以前的异常信息中有出错的点很容易就找到了,那是因为正巧异常源与第一次捕获的地方很接近
否则的话越是在调用的最上层捕获到异常,越不容易找到异常源在什么位置(这里都是在Release条件下)
在Release版本下,异常越早捕获,越能精确定位出错位置,越晚捕获,从堆栈中获得的获得的有效信息也越少,即使是用了throw;这样的方式也一样
严格禁止 下面这种写法:
try{
// do
}
catch(Exception ex)
{
throw ex;
}
如有必要需改用 throw; 或者 throw new SomeException(msg, ex); 这样的形式
如果是捕获了异常不想再次抛出(一般用在捕获到的异常在预期范围内,已经有相应的处理方式了),最好是能记录日志,表示发生过异常
附上一个测试:
Release下 使用throw;
Release下 使用throw ex;
Release下更极端的例子去掉了除最外层外所有的try…catch:
Debug下 使用throw;
Debug下 使用throw ex;