C# “throw”和“throw ex”之间有区别吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/730250/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 22:54:23  来源:igfitidea点击:

Is there a difference between "throw" and "throw ex"?

c#.netexceptionexception-handling

提问by dance2die

There are some posts that asks what the difference between those two are already.
(why do I have to even mention this...)

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

But my question is different in a way that I am calling "throw ex" in another error god-likehandling method.

但是我的问题在某种程度上有所不同,我在另一种类似错误之神的处理方法中称之为“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.
    }
}

If try & catchwere used in the Main, then I would use throw;to rethrow the error. But in the above simplied code, all exceptions go through HandleException

如果try & catch在 中使用Main,那么我将使用throw;重新抛出错误。但是在上面的简化代码中,所有的异常都经过HandleException

Does throw ex;has the same effect as calling throwwhen called inside HandleException?

在内部throw ex;调用throw时是否与调用具有相同的效果HandleException

采纳答案by Marc Gravell

Yes, there is a difference;

是,有一点不同;

  • throw exresets the stack trace (so your errors would appear to originate from HandleException)
  • throwdoesn't - the original offender would be preserved.

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }
    
  • throw ex重置堆栈跟踪(因此您的错误似乎源自HandleException
  • throw不 - 原始罪犯将被保留。

    static void Main(string[] args)
    {
        try
        {
            Method2();
        }
        catch (Exception ex)
        {
            Console.Write(ex.StackTrace.ToString());
            Console.ReadKey();
        }
    }
    
    private static void Method2()
    {
        try
        {
            Method1();
        }
        catch (Exception ex)
        {
            //throw ex resets the stack trace Coming from Method 1 and propogates it to the caller(Main)
            throw ex;
        }
    }
    
    private static void Method1()
    {
        try
        {
            throw new Exception("Inside Method1");
        }
        catch (Exception)
        {
            throw;
        }
    }
    

回答by Lucero

No, this will cause the exception to have a different stack trace. Only using a throwwithout any exception object in the catchhandler will leave the stack trace unchanged.

不,这将导致异常具有不同的堆栈跟踪。只有throwcatch处理程序中使用没有任何异常的对象才会保持堆栈跟踪不变。

You may want to return a boolean from HandleException whether the exception shall be rethrown or not.

无论是否重新抛出异常,您可能希望从 HandleException 返回一个布尔值。

回答by GR7

When you do throw ex, that thrown exception becomes the "original" one. So all previous stack trace will not be there.

当您这样做throw ex时,抛出的异常将成为“原始”异常。所以之前的所有堆栈跟踪都不会存在。

If you do throw, the exception just goes down the lineand you'll get the full stack trace.

如果你这样做throw,除了刚刚过去的路线,你会得到完整的堆栈跟踪。

回答by Shaul Behr

(I posted earlier, and @Marc Gravell has corrected me)

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

Here's a demonstration of the difference:

这是差异的演示:

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
}

and here is the output:

这是输出:

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

You can see that in Exception 1, the stack trace goes back to the DivByZero()method, whereas in Exception 2 it does not.

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

Take note, though, that the line number shown in ThrowException1()and ThrowException2()is the line number of the throwstatement, notthe line number of the call to DivByZero(), which probably makes sense now that I think about it a bit...

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

Output in Release mode

释放模式下的输出

Exception 1:

例外 1:

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

Exception 2:

例外 2:

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

Is it maintains the original stackTrace in debug mode only?

它是否仅在调试模式下维护原始堆栈跟踪?

回答by Jeppe Stig Nielsen

The other answers are entirely correct, but this answer provides some extra detalis, I think.

其他答案完全正确,但我认为这个答案提供了一些额外的细节。

Consider this example:

考虑这个例子:

using System;

static class Program {
  static void Main() {
    try {
      ThrowTest();
    } catch (Exception e) {
      Console.WriteLine("Your stack trace:");
      Console.WriteLine(e.StackTrace);
      Console.WriteLine();
      if (e.InnerException == null) {
        Console.WriteLine("No inner exception.");
      } else {
        Console.WriteLine("Stack trace of your inner exception:");
        Console.WriteLine(e.InnerException.StackTrace);
      }
    }
  }

  static void ThrowTest() {
    decimal a = 1m;
    decimal b = 0m;
    try {
      Mult(a, b);  // line 34
      Div(a, b);   // line 35
      Mult(b, a);  // line 36
      Div(b, a);   // line 37
    } catch (ArithmeticException arithExc) {
      Console.WriteLine("Handling a {0}.", arithExc.GetType().Name);

      //   uncomment EITHER
      //throw arithExc;
      //   OR
      //throw;
      //   OR
      //throw new Exception("We handled and wrapped your exception", arithExc);
    }
  }

  static void Mult(decimal x, decimal y) {
    decimal.Multiply(x, y);
  }
  static void Div(decimal x, decimal y) {
    decimal.Divide(x, y);
  }
}

If you uncomment the throw arithExc;line, your output is:

如果取消注释该throw arithExc;行,则输出为:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 44
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

Certainly, you have lost information about where that exception happened. If instead you use the throw;line, this is what you get:

当然,您已经丢失了有关异常发生位置的信息。相反,如果您使用该throw;行,这就是您得到的:

Handling a DivideByZeroException.
Your stack trace:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 46
   at Program.Main() in c:\somepath\Program.cs:line 9

No inner exception.

This is a lot better, because now you see that it was the Program.Divmethod that caused you problems. But it's still hard to see if this problem comes from line 35 or line 37 in the tryblock.

这好多了,因为现在你看到是Program.Div方法导致了你的问题。但是还是很难看出这个问题是出自try块中的第 35 行还是第 37 行。

If you use the third alternative, wrapping in an outer exception, you lose no information:

如果您使用第三种选择,包装在外部异常中,则不会丢失任何信息:

Handling a DivideByZeroException.
Your stack trace:
   at Program.ThrowTest() in c:\somepath\Program.cs:line 48
   at Program.Main() in c:\somepath\Program.cs:line 9

Stack trace of your inner exception:
   at System.Decimal.FCallDivide(Decimal& d1, Decimal& d2)
   at System.Decimal.Divide(Decimal d1, Decimal d2)
   at Program.Div(Decimal x, Decimal y) in c:\somepath\Program.cs:line 58
   at Program.ThrowTest() in c:\somepath\Program.cs:line 35

In particular you can see that it's line 35that leads to the problem. However, this requires people to search the InnerException, and it feels somewhat indirect to use inner exceptions in simple cases.

特别是您可以看到导致问题的是第 35 行。但是,这需要人们搜索InnerException,在简单的情况下使用内部异常感觉有些间接。

In this blog postthey preserve the line number (line of the try block) by calling (through reflection) the internalintance method InternalPreserveStackTrace()on the Exceptionobject. But it's not nice to use reflection like that (the .NET Framework might change their internalmembers some day without warning).

这篇博文中,他们通过调用(通过反射)对象上的internal实例方法InternalPreserveStackTrace()来保留行号(try 块的行)Exception。但是这样使用反射并不好(.NET Framework 可能internal有一天会在没有警告的情况下更改其成员)。

回答by Aaaaaaaa

Look at here: http://blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

看这里:http: //blog-mstechnology.blogspot.de/2010/06/throw-vs-throw-ex.html

Throw:

try 
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw;
}

It preserve the Stack information with Exception

它使用异常保留堆栈信息

This is called as "Rethrow"

这被称为“重新抛出”

If want to throw new exception,

如果想抛出新的异常,

throw new ApplicationException("operation failed!");

Throw Ex:

抛出

try
{
    // do some operation that can fail
}
catch (Exception ex)
{
    // do some local cleanup
    throw ex;
}

It Won't Send Stack information with Exception

它不会发送带有异常的堆栈信息

This is called as "Breaking the Stack"

这被称为“打破堆栈”

If want to throw new exception,

如果想抛出新的异常,

throw new ApplicationException("operation failed!",ex);

回答by Bhanu pratap

int a = 0;
try {
    int x = 4;
    int y ;
    try {
        y = x / a;
    } catch (Exception e) {
        Console.WriteLine("inner ex");
        //throw;   // Line 1
        //throw e;   // Line 2
        //throw new Exception("devide by 0");  // Line 3
    }
} catch (Exception ex) {
    Console.WriteLine(ex);
    throw ex;
}
  1. if all Line 1 ,2 and 3 are commented - Output - inner ex

  2. if all Line 2 and 3 are commented - Output - inner ex System.DevideByZeroException: {"Attempted to divide by zero."}---------

  3. if all Line 1 and 2 are commented - Output - inner ex System.Exception: devide by 0 ----

  4. if all Line 1 and 3 are commented - Output - inner ex System.DevideByZeroException: {"Attempted to divide by zero."}---------

  1. 如果第 1 行、第 2 行和第 3 行都被注释了 - 输出 - 内部前

  2. 如果第 2 行和第 3 行都被注释了 - 输出 - 内部 ex System.DevideByZeroException: {"试图除以零。"}---------

  3. 如果所有第 1 行和第 2 行都被注释 - 输出 - 内部 ex System.Exception: 除以 0 ----

  4. 如果第 1 行和第 3 行都被注释了 - 输出 - 内部 ex System.DevideByZeroException: {"试图除以零。"}---------

and StackTrace will be reset in case of throw ex;

并且 StackTrace 将在 throw ex 的情况下重置;

回答by Charles Owen

To give you a different perspective on this, using throw is particularly useful if you're providing an API to a client and you want to provide verbose stack trace information for your internal library. By using throw here, I'd get the stack trace in this case of the System.IO.File library for File.Delete. If I use throw ex, then that information will not be passed to my handler.

为了让您对此有不同的看法,如果您向客户端提供 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
    }
}

回答by Mahesh

let's understand the difference between throw and throw ex. I heard that in many .net interviews this common asked is being asked.

让我们了解 throw 和 throw ex 之间的区别。我听说在许多 .net 采访中都会问这个常见的问题。

Just to give an overview of these two terms, throw and throw ex are both used to understand where the exception has occurred. Throw ex rewrites the stack trace of exception irrespective where actually has been thrown.

为了概述这两个术语,throw 和 throw ex 都用于理解异常发生的位置。Throw ex 重写异常的堆栈跟踪,而不管实际抛出的位置。

Let's understand with an example.

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

Let's understand first Throw.

让我们先了解一下 Throw。

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();
}

output of the above is below.

上面的输出如下。

shows complete hierarchy and method name where actually the exception has thrown.. it is M2 -> M2. along with line numbers

显示了实际抛出异常的完整层次结构和方法名称..它是 M2 -> M2。连同行号

enter image description here

在此处输入图片说明

Secondly.. lets understand by throw ex. Just replace throw with throw ex in M2 method catch block. as below.

其次..让我们通过throw ex来理解。只需在 M2 方法 catch 块中用 throw ex 替换 throw 即可。如下。

enter image description here

在此处输入图片说明

output of throw ex code is as below..

throw ex代码的输出如下..

enter image description here

在此处输入图片说明

You can see the difference in the output.. throw ex just ignores all the previous hierarchy and resets stack trace with line/method where throw ex is written.

您可以看到输出中的差异.. throw ex 只是忽略所有以前的层次结构,并使用写入 throw ex 的行/方法重置堆栈跟踪。

回答by A.S.

MSDN stands for:

MSDN 代表

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.

一旦抛出异常,它携带的部分信息就是堆栈跟踪。堆栈跟踪是方法调用层次结构的列表,从抛出异常的方法开始,到捕获异常的方法结束。如果通过在 throw 语句中指定异常来重新抛出异常,则堆栈跟踪将在当前方法处重新启动,并且抛出异常的原始方法与当前方法之间的方法调用列表将丢失。要保留异常的原始堆栈跟踪信息,请使用 throw 语句而不指定异常。