~每天一个历史遗留问题~熟悉异常的 C++++ coder,都知道
对于 throw ex; 与 throw; 是有区别的,主要是 CLR 确定异常抛出的起点是有区别的,
如果你不清楚请参考:
《.NET 框架程序设计》Jeffery Richter 李建忠译 chater 18.12 异常堆栈踪迹 P442 的说明或者见
throw;与throw ex;之间的区别 
http://blog.csdn.net/Joy_Zhao/archive/2006/10/27/1352777.aspx
但是,下面的 Button1_Click 即使使用 throw; 也堆栈信息只能跟踪到 Line 14 ,始终无法跟踪到 Line 11,而 Button2_Click 直接到 Line 21// .aspx.cs
protected void Button1_Click(object sender, EventArgs e)
    {
        try {
            object o = null;
            int i = (int)o;  // Line 11  // Error, System.NullReferenceException       
        }
        catch {
            throw; // Line 14
        }       
    }    protected void Button2_Click(object sender, EventArgs e)
    {
        object o = null;
        int i = (int)o;  // Line 21  // Error, System.NullReferenceException  
    }// .aspx
 <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Throw1" />  
        <asp:Button ID="Button2" runat="server" OnClick="Button2_Click" Text="Throw2" />  
我做过很多测试了,对于用 try-catch-throw 捕获 CLR 运行时内部抛出的异常,即出现这种情况,始终无法跟踪到最原始的内部异常起点
何解 ?也许是我遗漏了某个知识点~谢谢!

解决方案 »

  1.   

    winform测试.
    单步可以调试到
    int i = (int)o;  // Line 11  // Error, System.NullReferenceException       
      

  2.   

    winform 同样有此不解问题单步,不能算~
      

  3.   

    没有太多的研究,一直认为只要有throw的语名的行,就是有出错信息的行了。
      

  4.   

    //这样就能跟踪到你想要的位置(Line11==Line4);
     protected void throwErr()
        {
            object o = null;
            int i = (int)o; //Line4  //Error, System.NullReferenceException  
        }    protected void Button1_Click(object sender, EventArgs e)
        {
            try
            {
                throwErr();
            }
            catch
            {
                throw;
            }
        }    protected void Button2_Click(object sender, EventArgs e)
        {
            object o = null;
            int i = (int)o;  // Line 12  // Error, System.NullReferenceException  
        }
      

  5.   

    @justcode(小码) 这个我知道,
    我想知道的是,底层方法如果加了 try-catch-throw,上层捕获,为何到不了 CLR 抛的异常起点,这点对于多层调用来说,调试很麻烦,
    目前我采取的方式,只有手动包装一次异常// DAL
    using(SqlConnection conn = new SqlConnection())
    try {
        // ...
        // ... // here, some error occurs at line 911
        // ...
    }
    catch(SqlException ex) {
        throw new MyCustomApplictionException("some unknow error under db operation.", ex);  // 这样顶层捕获,访问内部异常(这里即 ex),是可以追中到 line 911 的
    }
    也许我应该提供这样的测试实例:    protected void Button1_Click(object sender, EventArgs e)
        {
            try {
                ThrowException1();
            }
            catch(Exception ex) {
                Response.Write(ex.StackTrace); // 这里只能到 line 40, 我希望的是 line 37
                //Console.WriteLine(ex.StackTrace);        
                //throw;
            }
            
        }    protected void Button2_Click(object sender, EventArgs e)
        {        try {
                ThrowException2();
            }
            catch(Exception ex) {
                Response.Write(ex.StackTrace);
                //Console.WriteLine(ex.StackTrace);
                //throw;
            }
        }    private void ThrowException1()
        {
            try {
                object o = null;
                int i = (int)o; // line 37
            }
            catch {
                throw; // line 40
            }
        }
        private void ThrowException2()
        {
            object o = null;
            int i = (int)o;
        }
    Any way, thanks a lot.
      

  6.   

    这个到的确是奇怪,有没有在catch那里把异常输出看看堆栈是什么?
      

  7.   

    测试无法重现问题,.NET Framework 2.0
      

  8.   

    关于异常处理。。一直很让我头疼。。
    我只知道
    try{}catch(){} 是直接把异常抛出。

    try{}catch(Exception ex){}是要先把异常实例化的,这个过程应该是拿到了很确凿的数据,所以这样是可以追踪到出错的位置的,ex.StackTrace 。
    所以如果你想追踪异常的原始位置的话,应该写成后者,但这样效率会有所降低。。
      

  9.   

    @commandosvvv(UnknownData)事实上,你刚好理解反了
    try{}catch(){}   不会再抛出任何异常try{}catch(throw;){} , try{}catch(Exception)(throw;){} , try{}catch(Exception ex)(throw;){} 是等价的try{}catch(Exception ex)(throw;){} 与 try{}catch(Exception ex)(throw ex;){}
    几乎是相同的,只是后者 CLR 会重新设定异常起点,前者 IL 指令是 rethrow 后者 IL 指令是 throw这里我要讨论的是,在我的测试场景中,throw; 与 throw ex; 竟然相同了,输出的堆栈信息是一样的。测试实例:    protected void Button1_Click(object sender, EventArgs e)
        {
            try {
                ThrowException1();
            }
            catch(Exception ex) {
                Response.Write(ex.StackTrace); // 这里只能到 line 40, 我希望的是 line 37
                //Console.WriteLine(ex.StackTrace);        
                //throw;
            }
            
        }    protected void Button2_Click(object sender, EventArgs e)
        {        try {
                ThrowException2();
            }
            catch(Exception ex) {
                Response.Write(ex.StackTrace);
                //Console.WriteLine(ex.StackTrace);
                //throw;
            }
        }    private void ThrowException1()
        {
            try {
                object o = null;
                int i = (int)o; // line 37
            }
            catch {
                throw; // line 40
            }
        }
        private void ThrowException2()
        {
            object o = null;
            int i = (int)o;
        }
    Any way, thanks a lot.
      

  10.   

    这个到的确是奇怪,有没有在catch那里把异常输出看看堆栈是什么?=========@Ivony(授人以鱼不如授人以渔,上海谋生) 我打印出来看过了的,ThrowException1() 和 ThrowException2() 用 Console.WriteLine(ex.StackTrace)  总是有差别,ThrowException1() 无法到原始异常的起点这一行:  int i = (int)o; // line 37
      

  11.   

    你这个到底是WinForm的还是控制台的?不过我用两个都测试过了,没有发现你说的问题。
      

  12.   

    WinForm WebForm Console 都是一样的,我测过了
      

  13.   

    难不成见鬼了,你把Console的全部代码贴出来。是.NET Framework 2.0么,说说你的测试环境……
      

  14.   


    事实上,我是以前使用 Data Access Block SqlHelper 的使用,发现了的这个问题因为 SqlHelper 里面每个 ExecuteXXX 方法都包含了 try-catch-throw,我发现调用 ExecuteXXX 的方法,顶层捕获异常,堆栈信息只记录到 ExecuteXXX 方法的 throw 那一行,而无法到真正抛出异常的那一行,然后 ExecuteXXX 方法又是调用多个版本的重载方法,结果我就不知道到底是哪个方法,哪行代码错误了,我只有使用 try{ SqlHelper.ExecuteXXX } catch(SqlException ex) { throw new Exception("db operation failed.", ex); } 将内部异常包装一次可能,我还是无法描述清楚,回去把我的测试代码上传,供大家测试看看吧谢谢。
      

  15.   

    SQL Helper很垃圾的,推荐你到我的Blog(http://Ivony.cnblogs.com/)上去看看,尽管不知道谁在那里说垃圾,但我想比SQL Helper还是要强一点点的。
      

  16.   

    学习!~~
    不过,请教下,
    try{}catch(){throw;} 与try{}catch(Exception ex){throw ex;}
    只是 你所说得 “前者 IL 指令是 rethrow 后者 IL 指令是 throw”的区别吗??还有,什么都不写 自动有系统踢出不可继续进行的error 和使用try{}catch(){throw;}
    有什么区别呢??
      

  17.   

    我用.NET 2.0 能重复出LZ的情况. 这里边的问题实际上是Throw不能百分之百保证原始的Stack Trace.在网上Google了下,这个问题在MS的下边这个Group里讨论过:microsoft.public.dotnet.languages.csharp 讨论的题目是:"Bug?! throw vs. throw ex Options"里边有各种意见,我比较倾向于这是.NET 的一个Bug. 目前的对策也是和LZ一样,重新包装一下Exception以保证Stack Trace显示正确的行号.
      

  18.   

    @commandosvvv(UnknownData) 
    关于 throw ex; 与 throw; 的区别,我顶楼已经提供了链接文章参考了
    @RedGoldFish(红金鱼) 
    如果真是BUG,不知道 MS 何时修复对于异常处理情况十分的复杂,对于自己系统的底层框架,有时候我们只是希望捕获异常,然后执行一些资源释放的操作,重新丢出异常,并希望保持堆栈信息,一遍尽快查找错误,重新包装一次异常,给我们程序员带来了而外工作,有时候,我就真的不希望此异常做说明解释或者说明,直接继续向上抛,留给上层应用程序处理
      

  19.   

    一个完整的测试示例,诸位可以看看输出的 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
                    }
                }
            }
        }
    }