有一些帖子问这两者之间已经有什么区别了。(为什么我要提这个…)

但我的问题在某种程度上是不同的,我在另一种错误处理方法中调用了“throw ex”。

public class Program {
    public static void Main(string[] args) {
        try {
            // something
        } catch (Exception ex) {
            HandleException(ex);
        }
    }

    private static void HandleException(Exception ex) {
        if (ex is ThreadAbortException) {
            // ignore then,
            return;
        }
        if (ex is ArgumentOutOfRangeException) { 
            // Log then,
            throw ex;
        }
        if (ex is InvalidOperationException) {
            // Show message then,
            throw ex;
        }
        // and so on.
    }
}

如果在主线中使用try和catch,那么我会使用throw;重新抛出错误。 但是在上面的简单代码中,所有异常都通过HandleException

是否抛出前任;在HandleException内部调用时,与调用throw有相同的效果?


当前回答

为了让您从不同的角度来看待这个问题,如果您向客户端提供API,并且希望为内部库提供详细的堆栈跟踪信息,则使用throw特别有用。通过在这里使用throw,我可以获得File.Delete的System.IO.File库的堆栈跟踪。如果我使用throw ex,那么该信息将不会传递给我的处理程序。

static void Main(string[] args) {            
   Method1();            
}

static void Method1() {
    try {
        Method2();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method1");             
    }
}

static void Method2() {
    try {
        Method3();
    } catch (Exception ex) {
        Console.WriteLine("Exception in Method2");
        Console.WriteLine(ex.TargetSite);
        Console.WriteLine(ex.StackTrace);
        Console.WriteLine(ex.GetType().ToString());
    }
}

static void Method3() {
    Method4();
}

static void Method4() {
    try {
        System.IO.File.Delete("");
    } catch (Exception ex) {
        // Displays entire stack trace into the .NET 
        // or custom library to Method2() where exception handled
        // If you want to be able to get the most verbose stack trace
        // into the internals of the library you're calling
        throw;                
        // throw ex;
        // Display the stack trace from Method4() to Method2() where exception handled
    }
}

其他回答

微软文档代表:

Once an exception is thrown, part of the information it carries is the stack trace. The stack trace is a list of the method call hierarchy that starts with the method that throws the exception and ends with the method that catches the exception. If an exception is re-thrown by specifying the exception in the throw statement, the stack trace is restarted at the current method and the list of method calls between the original method that threw the exception and the current method is lost. To keep the original stack trace information with the exception, use the throw statement without specifying the exception.

来源:https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2200

Throw保存堆栈跟踪。假设Source1抛出Error1,它被Source2捕获,而Source2表示抛出,那么在堆栈跟踪中Source1 Error + Source2 Error将可用。

Throw ex不保留堆栈跟踪。因此Source1的所有错误将被清除,只有Source2的错误将被发送到客户端。

有时只是阅读的东西不清楚,建议看这个视频演示,以获得更清楚,在c# Throw vs Throw ex。

(我之前发过,@Marc Gravell纠正了我)

以下是其中的区别:

static void Main(string[] args) {
    try {
        ThrowException1(); // line 19
    } catch (Exception x) {
        Console.WriteLine("Exception 1:");
        Console.WriteLine(x.StackTrace);
    }
    try {
        ThrowException2(); // line 25
    } catch (Exception x) {
        Console.WriteLine("Exception 2:");
        Console.WriteLine(x.StackTrace);
    }
}

private static void ThrowException1() {
    try {
        DivByZero(); // line 34
    } catch {
        throw; // line 36
    }
}
private static void ThrowException2() {
    try {
        DivByZero(); // line 41
    } catch (Exception ex) {
        throw ex; // line 43
    }
}

private static void DivByZero() {
    int x = 0;
    int y = 1 / x; // line 49
}

这是输出:

Exception 1:
   at UnitTester.Program.DivByZero() in <snip>\Dev\UnitTester\Program.cs:line 49
   at UnitTester.Program.ThrowException1() in <snip>\Dev\UnitTester\Program.cs:line 36
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 19

Exception 2:
   at UnitTester.Program.ThrowException2() in <snip>\Dev\UnitTester\Program.cs:line 43
   at UnitTester.Program.TestExceptions() in <snip>\Dev\UnitTester\Program.cs:line 25

您可以看到,在例外1中,堆栈跟踪返回到DivByZero()方法,而在例外2中则没有。

但是请注意,在ThrowException1()和ThrowException2()中显示的行号是throw语句的行号,而不是调用DivByZero()的行号,现在我稍微思考一下,这可能是有意义的……

释放模式输出

例外1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)

例外2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)

它是否只在调试模式下维护原始的stackTrace ?

最好使用throw而不是throw ex。

Throw ex重置原始堆栈跟踪,无法找到之前的堆栈跟踪。

如果使用throw,我们将得到一个完整的堆栈跟踪。

让我们来理解throw和throw ex之间的区别。我听说在很多。net面试中这个常见的问题被问到。

简单介绍一下这两个术语,throw和throw ex都用于理解异常发生的位置。Throw ex重写异常的堆栈跟踪,而不考虑实际抛出的位置。

让我们通过一个例子来理解。

让我们先理解投掷。

static void Main(string[] args) {
    try {
        M1();
    } catch (Exception ex) {
        Console.WriteLine(" -----------------Stack Trace Hierarchy -----------------");
        Console.WriteLine(ex.StackTrace.ToString());
        Console.WriteLine(" ---------------- Method Name / Target Site -------------- ");
        Console.WriteLine(ex.TargetSite.ToString());
    }
    Console.ReadKey();
}

static void M1() {
    try {
        M2();
    } catch (Exception ex) {
        throw;
    };
}

static void M2() {
    throw new DivideByZeroException();
}

上面的输出如下。

显示完整的层次结构和方法名称,实际上已经抛出异常。它是M2 -> M2。还有行号

其次……让我们理解throw ex。只需在M2方法catch块中将throw替换为throw ex。如下。

throw ex代码的输出如下。

你可以在输出中看到差异。Throw ex只是忽略了之前的所有层次结构,并使用写入Throw ex的line/method重置堆栈跟踪。