留住异常的堆栈信息【throw ex 和 throw 的区别】
当我们想把框架底层的异常向上抛时(比如从DAL到BLL),例如下面的代码
try
{
//提交数据库
}
catch (System.Data.SqlClient.SqlException ex)
{
throw ex;
}
finally
{
//释放资源
}
{
//提交数据库
}
catch (System.Data.SqlClient.SqlException ex)
{
throw ex;
}
finally
{
//释放资源
}
上面代码的的问题在于,会造成CLR重新设置异常抛出的起点,导致我们在查看异常堆栈的时候,无法知道实际抛出异常的最底层的调用。
所以上面的代码应该使用throw关键字来向上抛出异常。
所以上面的代码应该使用throw关键字来向上抛出异常。
一个完整的测试示例,诸位可以看看输出的 StackTrace 就明白,各种使用 try-catch-throw 形式的不同点了:
using System;
using System.Collections.Generic;
using System.Text;
namespace TryCatchThrowTestConsole
{
class Program
{
static void Main(string[] args)
{
Tester tester = new Tester();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
/*
* 说明
* 1. 以下 ThrowEx_ 两两成对比较
* 2. ThrowEx7 和 ThrowEx8 演示已知的 throw; 与 throw ex; 的不同,即后者 CLR 重新设置异常的起点
* 位于异常捕获点之上中的调用堆栈信息不会包含在 Exception 的 StackTrace 属性中
* 3. ThrowEx1 和 ThrowEx2 演示本讨论的直接目标:为什么 ThrowEx2 中使用 throw; 堆栈信息也无法包括到 F2_1 这一行,而是直接到 F2_2
* 4. ThrowEx3 和 ThrowEx4 情形同 ThrowEx1 和 ThrowEx2
* 5. ThrowEx5 与 ThrowEx6 效果相同。
* 6. try{//...}catch{throw;}, try{//...}catch(Exception){throw;}, try{//...}catch(Exception ex){throw;} 三者是等价的
*/
try {
tester.ThrowEx1();
}
catch (Exception ex) {
sb.AppendLine( "1 ");
sb.AppendLine(ex.StackTrace); // 堆栈信息如期到 F1_1
sb.AppendLine();
}
try {
tester.ThrowEx2();
}
catch (Exception ex) {
// 堆栈信息只能到 F2_2,我的疑问是为什么不能到 F2_1
// ThrowEx2() 与 ThrowEx1() 唯一区别就是 ThrowEx1() 内部还有一个 try-catch-throw
sb.AppendLine( "2 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
//sb.AppendLine( "----------- ");
//sb.AppendLine(System.Environment.StackTrace);
//sb.AppendLine( "----------- ");
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(ex);
sb.AppendLine( "----------- ");
sb.AppendLine(st.ToString());
sb.AppendLine( "----------- ");
}
try {
tester.ThrowEx3();
}
catch (Exception ex) {
sb.AppendLine( "3 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx4();
}
catch (Exception ex) {
sb.AppendLine( "4 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx5();
}
catch (Exception ex) {
sb.AppendLine( "5 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx6();
}
catch (Exception ex) {
sb.AppendLine( "6 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx7();
}
catch (Exception ex) {
sb.AppendLine( "7 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx8();
}
catch (Exception ex) {
sb.AppendLine( "8 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
string result = sb.ToString();
System.IO.File.WriteAllText( "result.txt ", result); // 输出到文件
Console.WriteLine(result); // 输出到控制台
Console.Write( "press any key to exit... ");
Console.ReadKey();
}
public class Tester
{
public void ThrowEx1()
{
int i = 0, j = 0;
int k = i / j; // F1_1
}
public void ThrowEx2()
{
try {
int i = 0, j = 0;
int k = i / j; // F2_1
}
catch { // 这里 try{}catch(DivideByZeroException){throw;}, try{}catch(DivideByZeroException ex){throw;} 等价
throw; // F2_2
}
}
public void ThrowEx3()
{
throw new DivideByZeroException(); // F3_1
}
public void ThrowEx4()
{
try {
throw new DivideByZeroException(); // F4_1
}
catch {
throw; // F4_2
}
}
public void ThrowEx5()
{
ThrowEx1(); // F5_1
}
public void ThrowEx6()
{
try {
ThrowEx1(); // F6_1
}
catch {
throw; // F6_2
}
}
public void ThrowEx7()
{
try {
ThrowEx1(); // F7_1
}
catch (DivideByZeroException) { // 这里与 try{}catch{throw;}, try{}catch((DivideByZeroException ex){throw;} 等价
throw; // F7_2
}
}
public void ThrowEx8()
{
try {
ThrowEx1(); // F8_1
}
catch (DivideByZeroException ex) {
throw ex; // F8_2
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace TryCatchThrowTestConsole
{
class Program
{
static void Main(string[] args)
{
Tester tester = new Tester();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
/*
* 说明
* 1. 以下 ThrowEx_ 两两成对比较
* 2. ThrowEx7 和 ThrowEx8 演示已知的 throw; 与 throw ex; 的不同,即后者 CLR 重新设置异常的起点
* 位于异常捕获点之上中的调用堆栈信息不会包含在 Exception 的 StackTrace 属性中
* 3. ThrowEx1 和 ThrowEx2 演示本讨论的直接目标:为什么 ThrowEx2 中使用 throw; 堆栈信息也无法包括到 F2_1 这一行,而是直接到 F2_2
* 4. ThrowEx3 和 ThrowEx4 情形同 ThrowEx1 和 ThrowEx2
* 5. ThrowEx5 与 ThrowEx6 效果相同。
* 6. try{//...}catch{throw;}, try{//...}catch(Exception){throw;}, try{//...}catch(Exception ex){throw;} 三者是等价的
*/
try {
tester.ThrowEx1();
}
catch (Exception ex) {
sb.AppendLine( "1 ");
sb.AppendLine(ex.StackTrace); // 堆栈信息如期到 F1_1
sb.AppendLine();
}
try {
tester.ThrowEx2();
}
catch (Exception ex) {
// 堆栈信息只能到 F2_2,我的疑问是为什么不能到 F2_1
// ThrowEx2() 与 ThrowEx1() 唯一区别就是 ThrowEx1() 内部还有一个 try-catch-throw
sb.AppendLine( "2 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
//sb.AppendLine( "----------- ");
//sb.AppendLine(System.Environment.StackTrace);
//sb.AppendLine( "----------- ");
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace(ex);
sb.AppendLine( "----------- ");
sb.AppendLine(st.ToString());
sb.AppendLine( "----------- ");
}
try {
tester.ThrowEx3();
}
catch (Exception ex) {
sb.AppendLine( "3 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx4();
}
catch (Exception ex) {
sb.AppendLine( "4 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx5();
}
catch (Exception ex) {
sb.AppendLine( "5 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx6();
}
catch (Exception ex) {
sb.AppendLine( "6 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx7();
}
catch (Exception ex) {
sb.AppendLine( "7 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
try {
tester.ThrowEx8();
}
catch (Exception ex) {
sb.AppendLine( "8 ");
sb.AppendLine(ex.StackTrace);
sb.AppendLine();
}
string result = sb.ToString();
System.IO.File.WriteAllText( "result.txt ", result); // 输出到文件
Console.WriteLine(result); // 输出到控制台
Console.Write( "press any key to exit... ");
Console.ReadKey();
}
public class Tester
{
public void ThrowEx1()
{
int i = 0, j = 0;
int k = i / j; // F1_1
}
public void ThrowEx2()
{
try {
int i = 0, j = 0;
int k = i / j; // F2_1
}
catch { // 这里 try{}catch(DivideByZeroException){throw;}, try{}catch(DivideByZeroException ex){throw;} 等价
throw; // F2_2
}
}
public void ThrowEx3()
{
throw new DivideByZeroException(); // F3_1
}
public void ThrowEx4()
{
try {
throw new DivideByZeroException(); // F4_1
}
catch {
throw; // F4_2
}
}
public void ThrowEx5()
{
ThrowEx1(); // F5_1
}
public void ThrowEx6()
{
try {
ThrowEx1(); // F6_1
}
catch {
throw; // F6_2
}
}
public void ThrowEx7()
{
try {
ThrowEx1(); // F7_1
}
catch (DivideByZeroException) { // 这里与 try{}catch{throw;}, try{}catch((DivideByZeroException ex){throw;} 等价
throw; // F7_2
}
}
public void ThrowEx8()
{
try {
ThrowEx1(); // F8_1
}
catch (DivideByZeroException ex) {
throw ex; // F8_2
}
}
}
}
}