13

我读到,在 catch 块中,我可以使用“throw;”重新抛出当前异常;或“扔前;”。

来自:http: //msdn.microsoft.com/en-us/library/ms182363%28VS.80%29.aspx

“要保留原始堆栈跟踪信息与异常,请使用 throw 语句而不指定异常。”

但是当我尝试这个时

        try{
            try{
                try{
                    throw new Exception("test"); // 13
                }catch (Exception ex1){
                    Console.WriteLine(ex1.ToString());
                    throw; // 16
                }
            }catch (Exception ex2){
                Console.WriteLine(ex2.ToString()); // expected same stack trace
                throw ex2; // 20
            }
        }catch (Exception ex3){
            Console.WriteLine(ex3.ToString());
        }

我得到三个不同的堆栈。我期待第一个和第二个跟踪是相同的。我究竟做错了什么?(或理解错误?)

System.Exception:在 c:\Program.cs:line 13 中的 ConsoleApplication1.Program.Main(String[] args) 处进行测试 System.Exception:在 c:\Program 中的 ConsoleApplication1.Program.Main(String[] args) 处进行测试。 cs:line 16 System.Exception: 在 c:\Program.cs:line 20 中的 ConsoleApplication1.Program.Main(String[] args) 测试

4

3 回答 3

7

throw如果您不从当前堆栈框架中抛出它,则只会保留堆栈框架。您正在通过一种方法完成所有这些操作来做到这一点。

看到这个答案:https ://stackoverflow.com/a/5154318/1517578

PS:+1 用于提出实际上是有效问题的问题。

于 2013-02-04T04:04:46.800 回答
2

Simon beat me to answering, but you can see the expected behaviour simply by throwing the original exception from inside another function:

static void Main(string[] args)
{
    try
    {
        try
        {
            try
            {
                Foo();
            }
            catch (Exception ex1)
            {
                Console.WriteLine(ex1.ToString());
                throw;
            }
        }
        catch (Exception ex2)
        {
            Console.WriteLine(ex2.ToString()); // expected same stack trace
            throw ex2;
        }
    }
    catch (Exception ex3)
    {
        Console.WriteLine(ex3.ToString());
    }
}

static void Foo()
{
    throw new Exception("Test2");
}
于 2013-02-04T04:10:29.483 回答
2

好吧,我在这个问题上挖掘了更多,这是我非常个人的结论:

永远不要使用“投掷”;但总是以指定的原因重新抛出一个新的异常。

这是我的推理:

我受到我之前使用 Java 的经验的影响,并期望 C# throw 与 Java 非常相似。好吧,我对这个问题进行了更多研究,以下是我的观察:

    static void Main(string[] args){
        try {
            try {
                throw new Exception("test"); // 13
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
                throw ex;// 17
            }
        } catch (Exception ex) {
            Console.WriteLine(ex.ToString());
        }
    }

产量:

System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17

直观地说,Java 程序员会期望这两个异常是相同的:

System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13

但是 C# 文档清楚地表明这是可以预期的:

“如果通过在 throw 语句中指定异常重新引发异常,则堆栈跟踪将在当前方法处重新启动,并且引发异常的原始方法与当前方法之间的方法调用列表将丢失。要将原始堆栈跟踪信息与异常一起保留,请使用 throw 语句而不指定异常。”</p>

现在,如果我稍微改变了测试(替换 throw ex; by throw; 在第 17 行)。

        try {
            try {
                throw new Exception("test"); // 13
            }
            catch (Exception ex) {
                Console.WriteLine(ex.ToString());
                throw;// 17
            }
        } catch (Exception ex) {
            Console.WriteLine(ex.ToString());
        }

产量:

System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17

显然这不是我所期望的(因为这是最初的问题)。我在第二个堆栈跟踪中丢失了原始抛出点。西蒙怀特黑德将解释联系起来,即投掷;仅当当前方法中未发生异常时才保留堆栈跟踪。所以在同一个方法中不带参数的“抛出”是没有用的,因为一般来说,它不会帮助你找到异常的原因。

做任何 Java 程序员都会做的事情,我将第 17 行的语句替换为:

throw new Exception("rethrow", ex);// 17

产量:

System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13

System.Exception: rethrow ---> System.Exception: test
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 13
  --- End of inner exception stack trace ---
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 17

这是一个更好的结果。

然后,我开始使用方法调用进行测试。

    private static void throwIt() {
        throw new Exception("Test"); // 10
    }

    private static void rethrow(){
        try{
            throwIt(); // 15
        } catch (Exception ex) {
            Console.WriteLine(ex.ToString());
            throw; // 18
        }
    }

    static void Main(string[] args){
        try{
            rethrow(); // 24
        } catch (Exception ex) {
            Console.WriteLine(ex.ToString());
        }
    }

同样,堆栈跟踪不是我(Java 程序员)所期望的。在 Java 中,两个堆栈都将是相同的,并且三个方法深度如下:

java.lang.Exception: Test
    at com.example.Test.throwIt(Test.java:10)
    at com.example.Test.rethrow(Test.java:15)
    at com.example.Test.main(Test.java:24)

第一个堆栈跟踪只有两个方法深度。

System.Exception: Test
  at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
  at ConsoleApplication1.Program.rethrow() in Program.cs:line 15

就像堆栈跟踪是作为堆栈展开过程的一部分填充的一样。如果我当时记录堆栈跟踪以调查异常,我可能会丢失重要信息。

第二个堆栈跟踪是三个方法深度,但第 18 行(抛出;)出现在其中。

System.Exception: Test
  at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
  at ConsoleApplication1.Program.rethrow() in Program.cs:line 18
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 24

这一观察与之前的观察相似:堆栈跟踪不会保留在当前方法范围内,并且我再次丢失了发生异常的调用方法。例如,如果 rethow 写成:

private static void rethrow(){
    try{
        if (test) 
            throwIt(); // 15
        else 
            throwIt(); // 17
    } catch (Exception ex) {
        Console.WriteLine(ex.ToString());
        throw; // 20
    }
}

产量

System.Exception: Test
  at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
  at ConsoleApplication1.Program.rethrow() in Program.cs:line 20
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26

哪个 throwIt() 调用引发了异常?Stack 说第 20 行,那么它是第 15 行还是第 17 行?

解决方案与上一个相同:将原因包装在一个新的异常中,该异常产生:

System.Exception: rethrow ---> System.Exception: Test
  at ConsoleApplication1.Program.throwIt() in Program.cs:line 10
  at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 17
  --- End of inner exception stack trace ---
  at ConsoleApplication1.Program.rethrow(Boolean test) in Program.cs:line 20
  at ConsoleApplication1.Program.Main(String[] args) in Program.cs:line 26

我对这一切的简单结论是永远不要使用“投掷”;但总是重新抛出具有指定原因的新异常。

于 2013-02-05T03:11:27.007 回答