"throw"和"throw ex"之间有区别吗?

dan*_*die 414 .net c# exception-handling exception

有些帖子询问这两者之间的区别是什么.
(为什么我还要提这个...)

但我的问题是不同的,我称之为"抛出前"在另一个错误的神像处理方法.

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.
    }
}
Run Code Online (Sandbox Code Playgroud)

如果try & catch用于Main,那么我会throw;用来重新抛出错误.但在上面简化的代码中,所有异常都会通过HandleException

在调用内部时调用throw ex;效果是否相同?throwHandleException

Mar*_*ell 651

是,有一点不同;

  • 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;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

  • 要扩展Marc的答案,您可以在此处找到更多详细信息:http://geekswithblogs.net/sdorman/archive/2007/08/20/Difference-between-quotthrowquot-and-quotthrow-exquot-in-.NET. ASPX (28认同)
  • 不,你是对的。在这种情况下,“ throw;”和“ throw ex;”抛出相同的对象,但其堆栈跟踪以不同的方式修改,正如您在答案中所解释的那样。 (4认同)
  • @Shaul; 不,不是.我在你的帖子评论中给出了详细信息. (3认同)
  • @Marc:如果throw不在抛出初始异常的方法中,似乎throw会保留原始的罪犯(请参阅此问题:http://stackoverflow.com/questions/5152265/what-c​​an-lead -throw-to-reset-a-callstack-im-using-throw-not-throw-ex) (3认同)
  • @ScottDorman博客迁移后,您的链接似乎未正确转发。看起来[现在住在这里](https://scottdorman.blog/2007/08/20/difference-between-throw-and-throw-ex-in-net/)。**编辑:**嘿,等等,那是您的博客!修复您自己的链接!; ^ D (2认同)

Sha*_*ica 94

(我之前发过,@ 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
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

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
Run Code Online (Sandbox Code Playgroud)

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

注意到,虽然,在显示的行数ThrowException1()ThrowException2()是的行号throw的语句,调用的行号DivByZero(),这可能是有道理的,现在想起来有点...

在发布模式下输出

例外1:

at ConsoleAppBasics.Program.ThrowException1()
at ConsoleAppBasics.Program.Main(String[] args)
Run Code Online (Sandbox Code Playgroud)

例外2:

at ConsoleAppBasics.Program.ThrowException2()
at ConsoleAppBasics.Program.Main(String[] args)
Run Code Online (Sandbox Code Playgroud)

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

  • 因为编译器的优化过程内联了`DevideByZero`等短方法,所以堆栈跟踪是一样的。也许你应该把它作为一个问题单独发布 (2认同)

Jep*_*sen 39

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

考虑这个例子:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

如果取消注释该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.
Run Code Online (Sandbox Code Playgroud)

当然,您丢失了有关异常发生位置的信息.如果你使用这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.
Run Code Online (Sandbox Code Playgroud)

这样做要好得多,因为现在你看到它是Program.Div导致你出问题的方法.但是仍然很难看出这个问题是来自该try区块的第35行还是第37行.

如果您使用第三个替代方法,包装在外部异常中,则不会丢失任何信息:

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
Run Code Online (Sandbox Code Playgroud)

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

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


Shi*_*ala 21

抛出保留堆栈跟踪。所以假设 Source1 抛出 Error1 ,它被 Source2 捕获并且 Source2 说 throw 那么 Source1 Error + Source2 Error 将在堆栈跟踪中可用。

抛出 ex不保留堆栈跟踪。所以 Source1 的所有错误都将被清除,只有 Source2 错误会发送到客户端。

有时只是阅读内容不清楚,建议观看此视频演示以获得更多清晰度,C# 中的 Throw 与 Throw ex

投掷与投掷前

  • 那里的图表非常漂亮:) (2认同)

GR7*_*GR7 8

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

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


Luc*_*ero 6

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

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


小智 6

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

为了概括这两个术语,throw和throw ex都用于了解异常发生的位置.抛出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();
}
Run Code Online (Sandbox Code Playgroud)

以上输出如下.

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

在此输入图像描述

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

在此输入图像描述

throw ex代码的输出如下.

在此输入图像描述

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


A.S*_*.S. 5

Microsoft Docs 代表

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

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