抛出和抛出新Exception()之间的区别


Answers:


259

throw; 重新抛出原始异常并保留其原始堆栈跟踪。

throw ex;引发原始异常,但会重置堆栈跟踪,破坏所有堆栈跟踪信息,直到您的catch块为止。


永远不要throw ex;


throw new Exception(ex.Message);更糟的是 它创建一个全新的Exception实例,丢失该异常的原始堆栈跟踪及其类型。(例如IOException)。
此外,某些例外情况还包含其他信息(例如ArgumentException.ParamName)。
throw new Exception(ex.Message); 也会破坏这些信息。

在某些情况下,您可能希望将所有异常包装在自定义异常对象中,以便可以提供有关引发异常时代码在做什么的其他信息。

为此,请定义一个继承的新类Exception添加所有四个异常构造函数,还可以选择一个InnerException同时包含和其他信息的附加构造函数,然后将新的异常类作为参数传递ex给您InnerException。通过传递original InnerException,可以保留所有原始异常的属性,包括堆栈跟踪。


24
“抛出新的Exception(ex);甚至更糟。”:我不同意这一点。有时您想要更改异常的类型,然后将原始异常保留为内部异常是您的最佳选择。虽然应该是这样throw new MyCustomException(myMessage, ex);
Dirk Vollmar 2010年

9
@ 0xA3执行:我的意思是ex.Message,这雪上加霜。
SLaks 2010年

6
除了实现标准的构造函数外,还应创建自定义异常[Serializable()]
Dirk Vollmar 2010年

21
da,我们将您喜欢的例外带给您,因此我们在您的例外中添加了例外,这样您就可以在捕捉的同时捕捉到。
达斯大陆

2
@SLaks:将throw;发生异常的实际行号替换为的行号throw;。您如何建议处理呢? stackoverflow.com/questions/2493779/...
埃里克J.

34

第一个保留原始的堆栈跟踪:

try { ... }
catch
{
    // Do something.
    throw;
}

第二个允许您更改异常和/或消息及其他数据的类型:

try { ... } catch (Exception e)
{
    throw new BarException("Something broke!");
}

还有第三种方法可以传递内部异常:

try { ... }
catch (FooException e) {
    throw new BarException("foo", e);
} 

我建议使用:

  • 第一个是要在错误情况下进行一些清理而又不破坏信息或添加有关错误的信息的情况。
  • 第三,如果您想添加有关该错误的更多信息。
  • 第二个是要隐藏信息(对不受信任的用户)。

6

我没有看到有人提出的另一点:

如果您在catch {}块中没有执行任何操作,那么try ... catch是没有意义的。我一直都这样看:

try 
{
  //Code here
}
catch
{
    throw;
}

或更糟的是:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw ex;
}

最糟糕的是:

try 
{
  //Code here
}
catch(Exception ex)
{
    throw new System.Exception(ex.Message);
}

我同意,除非您有finally子句。
托尼·罗斯曼

1
@ToniRossmann在这种情况下,我将尝试使用try..finally,但除非您正在执行除掷球以外的其他操作,否则,否则将不进行任何操作。
JLWarlow

4

throw重新引发捕获的异常,保留堆栈跟踪,而throw new Exception丢失捕获的异常的某些详细信息。

通常throw,您通常会单独使用它来记录异常,而此时并没有完全处理它。

BlackWasp有一篇很好的文章,标题为《C#中的Throwing Exceptions》


4

抛出新的Exception会破坏当前的堆栈跟踪。

throw;将保留原始堆栈跟踪,并且几乎总是更有用。该规则的例外是您希望将Exception包装在您自己的自定义Exception中。然后,您应该执行以下操作:

catch(Exception e)
{
    throw new CustomException(customMessage, e);
}

3

throw用于重新抛出捕获的异常。如果您想在将异常传递给调用链之前对异常进行某些操作,这将很有用。

throw不带任何参数使用将保留调用堆栈以用于调试。


0

如果需要,可以引发一个新的Exception,将原来的一个设置为内部异常。


0

您的第二个示例将重置异常的堆栈跟踪。第一个最准确地保留了异常的起源。另外,您已经解开了原始类型,这对于了解实际出了问题是很关键的...如果第二个功能是必需的-例如,要添加扩展信息或使用特殊类型(例如自定义“ HandleableException”)重新包装,则只需确保也设置了InnerException属性!


是的,这是您必须快速编写的问题之一。;)
Robert Harvey 2010年

0

最重要的区别是第二个表达式删除了异常类型。异常类型在捕获异常中起着至关重要的作用:

public void MyMethod ()
{
    // both can throw IOException
    try { foo(); } catch { throw; }
    try { bar(); } catch(E) {throw new Exception(E.message); }
}

(...)

try {
    MyMethod ();
} catch (IOException ex) {
    Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
    Console.WriteLine ("Other error");    // [2]
}

如果foo()抛出IOException[1]catch块将捕获异常。但是当bar()抛出时IOException,它将被转换为普通Exception蚂蚁,不会被[1]catch块捕获。


0

throw或throw ex,这两者都用于抛出或重新抛出异常,当您只是简单地记录错误信息而又不想将任何信息发送回给调用者时,您只需在catch和离开中记录错误。但是,如果您想将有关异常的一些有意义的信息发送给使用throw或throw ex的调用方。现在throw和throw ex之间的区别是throw保留了堆栈跟踪和其他信息,但是throw ex创建了新的异常对象,因此原始的堆栈跟踪丢失了。因此,何时应该使用throw and throw e,在某些情况下,您可能想重新抛出异常,例如重置调用堆栈信息。例如,如果该方法在库中,并且您想从调用代码中隐藏该库的详细信息,您不一定希望调用堆栈在库中包含有关私有方法的信息。在这种情况下,您可以在库的公共方法中捕获异常,然后重新抛出它们,以便调用堆栈从这些公共方法开始。


0

扔; 重新抛出原始异常并保留异常类型。

抛出新的exception(); 重新抛出原始异常类型并重置异常堆栈跟踪

前掷 重置异常堆栈跟踪并重置异常类型


-1

这里没有任何答案能显示出差异,这可能对努力了解差异的人们有所帮助。考虑以下示例代码:

using System;
using System.Collections.Generic;

namespace ExceptionDemo
{
   class Program
   {
      static void Main(string[] args)
      {
         void fail()
         {
            (null as string).Trim();
         }

         void bareThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw;
            }
         }

         void rethrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw e;
            }
         }

         void innerThrow()
         {
            try
            {
               fail();
            }
            catch (Exception e)
            {
               throw new Exception("outer", e);
            }
         }

         var cases = new Dictionary<string, Action>()
         {
            { "Bare Throw:", bareThrow },
            { "Rethrow", rethrow },
            { "Inner Throw", innerThrow }
         };

         foreach (var c in cases)
         {
            Console.WriteLine(c.Key);
            Console.WriteLine(new string('-', 40));
            try
            {
               c.Value();
            } catch (Exception e)
            {
               Console.WriteLine(e.ToString());
            }
         }
      }
   }
}

生成以下输出:

Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
   --- End of inner exception stack trace ---
   at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
   at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64

如先前的答案所示,该裸机清楚地显示了发生故障的原始代码行(第12行)以及异常发生时调用堆栈中活动的其他两个点(第19和64行)。

重投案例的输出说明了为什么会出现问题。像这样重新抛出异常时,该异常将不包含原始堆栈信息。请注意,仅包含throw e(第35行)和最外面的调用堆栈点(第64行)。如果您以这种方式抛出异常,将很难找到将fail()方法作为问题的根源。

最后一种情况(innerThrow)最详尽,并且比上述任何一种情况都包含更多信息。由于我们正在实例化一个新的异常,因此我们有机会添加上下文信息(“外部”消息,在这里,但我们也可以将新异常添加到.Data字典中),并将所有信息保留在原始信息中异常(包括帮助链接,数据字典等)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.