“抛出”和“抛出新异常()”之间的区别

Posted

技术标签:

【中文标题】“抛出”和“抛出新异常()”之间的区别【英文标题】:Difference between 'throw' and 'throw new Exception()' 【发布时间】:2011-03-01 06:27:08 【问题描述】:

有什么区别

try  ... 
catch throw 

try ... 
catch(Exception e) throw new Exception(e.message) 

不管第二个显示消息。

【问题讨论】:

第二个 sn-p 是我见过的最邪恶(但无害)的代码行之一。 ***.com/questions/22623/… 【参考方案1】:

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

throw ex; 抛出原始异常但重置堆栈跟踪,销毁所有堆栈跟踪信息,直到您的 catch 块。

绝不throw ex;

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

在某些情况下,您可能希望将所有异常包装在自定义异常对象中,以便提供有关引发异常时代码正在执行的操作的更多信息。

为此,请定义一个继承 Exceptionadd all four exception constructors 的新类,以及一个可选的附加构造函数,该构造函数采用 InnerException 以及附加信息,然后抛出新异常类,ex 作为InnerException 参数传递。通过传递原始的InnerException,您可以保留原始异常的所有属性,包括堆栈跟踪。

【讨论】:

"throw new Exception(ex); 更糟糕。":我不同意这一点。有时您想更改异常的类型,然后将原始异常保留为内部异常是您能做的最好的事情。当然应该是throw new MyCustomException(myMessage, ex); @0xA3:我的意思是ex.Message,这更糟。 除了实现标准构造函数外,还应该自定义异常[Serializable()] 老兄,我们喜欢异常,所以我们在你的异常中放了一个异常,这样你就可以在捕获的同时捕获。 @SLaks:当您throw; 时,发生异常的实际行号将替换为throw; 的行号。你建议如何处理? ***.com/questions/2493779/…【参考方案2】:

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

try  ... 
catch

    // Do something.
    throw;

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

try  ...  catch (Exception e)

    throw new BarException("Something broke!");

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

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

我建议使用:

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

【讨论】:

【参考方案3】:

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

如果你没有在你的 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 子句。 @ToniRossmann 在这种情况下,我会使用 try..finally 而不使用 catch,除非你正在做的事情不是 throw;【参考方案4】:

抛出一个新的异常会清除当前的堆栈跟踪。

throw; 将保留原始堆栈跟踪,并且几乎总是更有用。该规则的例外是当您想要将异常包装在您自己的自定义异常中时。然后你应该这样做:

catch(Exception e)

    throw new CustomException(customMessage, e);

【讨论】:

【参考方案5】:

throw 重新抛出捕获的异常,保留堆栈跟踪,而throw new Exception 丢失了捕获的异常的一些细节。

您通常会单独使用throw 来记录异常,而不是在此时完全处理它。

BlackWasp 有一篇很好的文章,标题足够 Throwing Exceptions in C#

【讨论】:

【参考方案6】:

这里的答案都没有显示差异,这可能对努力理解差异的人们有所帮助。考虑这个示例代码:

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 行) .

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

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

【讨论】:

不确定为什么在没有 cmets 的情况下被否决,因为它与最佳答案基本相同,但有更多细节。【参考方案7】:

throw 用于重新抛出捕获的异常。如果您想在将异常传递到调用链之前对其进行处理,这将很有用。

使用不带任何参数的throw 会保留调用堆栈以进行调试。

【讨论】:

【参考方案8】:

您的第二个示例将重置异常的堆栈跟踪。第一个最准确地保留了异常的起源。

您还解开了原始类型,这是了解实际问题的关键...如果功能需要第二个类型 - 例如,添加扩展信息或使用特殊类型(例如自定义“HandleableException”)重新包装那么只需确保也设置了 InnerException 属性!

【讨论】:

是的,这是你必须快速写作的问题之一。 ;)【参考方案9】:

如果你愿意,你可以抛出一个新的异常,将原来的异常设置为内部异常。

【讨论】:

【参考方案10】:

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

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] 捕获块将捕获异常。但是当bar() 抛出IOException 时,它会被转换为普通的Exception 并且不会被[1] catch 块捕获。

【讨论】:

【参考方案11】:

throwthrow ex,当您只是简单地记录错误信息并且不想发回任何信息时,两者都用于抛出或重新抛出异常对于调用者,您只需在 catch 中记录错误并离开。

但如果你想向调用者发送一些关于异常的有意义的信息,你可以使用 throw 或 throw ex。现在 throw 和 throw ex 的区别在于 throw 保留了堆栈跟踪和其他信息,但是 throw ex 创建了一个新的异常对象,因此原始堆栈跟踪丢失了。

那么我们什么时候应该使用throwthrow e呢?在某些情况下,您可能需要重新引发异常,例如重置调用堆栈信息。

例如,如果方法在库中,并且您想对调用代码隐藏库的详细信息,则不一定希望调用堆栈包含有关库中私有方法的信息。在这种情况下,您可以在库的公共方法中捕获异常,然后重新抛出它们,以便调用堆栈从这些公共方法开始。

【讨论】:

【参考方案12】:

Throw;:重新抛出原来的异常,保持异常类型。

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

Throw ex;:重置异常堆栈跟踪并重置异常类型

【讨论】:

以上是关于“抛出”和“抛出新异常()”之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

5.异常丢失

抑制异常和原因之间的区别

简述override和overload的区别

“throw(e)”和“throw e”之间的区别?

Override和Overload的区别

重写(覆盖,Override)和重载(Overload)的区别