'throw'and '

两者有什么区别

try { ... }
catch{ throw }

而且

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

不管第二个显示的是一条消息。

252254 次浏览

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

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


从来没有throw ex;


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

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

为此,定义一个继承Exception添加所有四个异常构造函数的新类,以及可选的接受InnerException和其他信息的附加构造函数,并抛出你的新异常类传递__ABC2作为InnerException参数。通过传递原始的InnerException,可以保留所有原始异常的属性,包括堆栈跟踪。

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

你通常会使用throw本身来记录异常,而不完全处理它。

BlackWasp有一篇题为在c#中抛出异常的好文章。

throw用于重新抛出捕获的异常。如果您想在将异常传递到调用链之前对其做一些事情,这可能很有用。

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

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

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

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

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

第一个保存原始的stacktrace:

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

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

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

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

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

我建议使用:

  • 如果您想在错误情况下做一些清理,而不破坏信息或添加关于错误的信息,则使用第一种方法。
  • 如果您想添加关于错误的更多信息,则使用第三个。
  • 如果您想隐藏信息(不受信任的用户),则使用第二种方法。

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

此外,您还打开了原始类型,这是了解实际出错的关键……如果第二个属性是功能所必需的——例如,添加扩展信息或用特殊类型(如自定义的'HandleableException')重新包装,那么只需确保InnerException属性也设置了!

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

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{}块中做任何事情,尝试一下…捕捉是没有意义的。我经常看到这样的情况:

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

把前女友,两者都用于抛出或重新抛出异常,当你只是简单地记录错误信息而不想将任何信息返回给调用者时,你只需在catch和leave中记录错误。

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

那么什么时候应该使用把e呢?在一些情况下,您可能需要重新抛出异常,例如重置调用堆栈信息。

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

Throw;:重新抛出原始异常并保持异常类型。

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

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

这里的答案都没有显示出差异,这可能有助于那些努力理解差异的人。考虑下面的示例代码:

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)是最详细的,包含的信息比上面任何一种都要多。因为我们正在实例化一个新的异常,所以我们有机会添加上下文信息(“outside”;message,但我们也可以在新异常上添加到. data字典中),以及保留原始异常中的所有信息(包括帮助链接、数据字典等)。