【发布时间】:2011-03-01 06:27:08
【问题描述】:
有什么区别
try { ... }
catch{ throw }
和
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
不管第二个显示消息。
【问题讨论】:
-
第二个 sn-p 是我见过的最邪恶(但无害)的代码行之一。
标签: c#
有什么区别
try { ... }
catch{ throw }
和
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
不管第二个显示消息。
【问题讨论】:
标签: c#
throw; 重新抛出原始异常并保留其原始堆栈跟踪。
throw ex; 抛出原始异常但重置堆栈跟踪,销毁所有堆栈跟踪信息,直到您的 catch 块。
throw ex;
throw new Exception(ex.Message); 更糟糕。它创建了一个全新的Exception 实例,丢失了异常的原始堆栈跟踪及其类型。 (例如,IOException)。
此外,一些异常包含额外的信息(例如,ArgumentException.ParamName)。throw new Exception(ex.Message); 也会破坏这些信息。
在某些情况下,您可能希望将所有异常包装在自定义异常对象中,以便提供有关引发异常时代码正在执行的操作的更多信息。
为此,请定义一个继承 Exception、add all four exception constructors 的新类,以及一个可选的附加构造函数,该构造函数采用 InnerException 以及附加信息,然后抛出新异常类,将ex 作为InnerException 参数传递。通过传递原始的InnerException,您可以保留原始异常的所有属性,包括堆栈跟踪。
【讨论】:
throw new MyCustomException(myMessage, ex);。
ex.Message,这更更糟。
[Serializable()]。
throw; 时,发生异常的实际行号将替换为throw; 的行号。你建议如何处理? stackoverflow.com/questions/2493779/…
第一个保留原始堆栈跟踪:
try { ... }
catch
{
// Do something.
throw;
}
第二个允许您更改异常和/或消息和其他数据的类型:
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
还有第三种传递内部异常的方法:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
我建议使用:
【讨论】:
我没有看到任何人提出的另一点:
如果你没有在你的 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);
}
【讨论】:
抛出一个新的异常会清除当前的堆栈跟踪。
throw; 将保留原始堆栈跟踪,并且几乎总是更有用。该规则的例外是当您想要将异常包装在您自己的自定义异常中时。然后你应该这样做:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
【讨论】:
throw 重新抛出捕获的异常,保留堆栈跟踪,而throw new Exception 丢失了捕获的异常的一些细节。
您通常会单独使用throw 来记录异常,而不是在此时完全处理它。
BlackWasp 有一篇很好的文章,标题足够 Throwing Exceptions in C#。
【讨论】:
这里的答案都没有显示差异,这可能对努力理解差异的人们有所帮助。考虑这个示例代码:
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 字典中)以及保留原始信息中的所有信息异常(包括帮助链接、数据字典等)。
【讨论】:
throw 用于重新抛出捕获的异常。如果您想在将异常传递到调用链之前对其进行处理,这将很有用。
使用不带任何参数的throw 会保留调用堆栈以进行调试。
【讨论】:
您的第二个示例将重置异常的堆栈跟踪。第一个最准确地保留了异常的起源。
您还解开了原始类型,这是了解实际问题的关键...如果功能需要第二个类型 - 例如,添加扩展信息或使用特殊类型(例如自定义“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 块捕获。
【讨论】:
throw 或 throw ex,当您只是简单地记录错误信息并且不想发回任何信息时,两者都用于抛出或重新抛出异常对于调用者,您只需在 catch 中记录错误并离开。
但如果你想向调用者发送一些关于异常的有意义的信息,你可以使用 throw 或 throw ex。现在 throw 和 throw ex 的区别在于 throw 保留了堆栈跟踪和其他信息,但是 throw ex 创建了一个新的异常对象,因此原始堆栈跟踪丢失了。
那么我们什么时候应该使用throw和throw e呢?在某些情况下,您可能需要重新引发异常,例如重置调用堆栈信息。
例如,如果方法在库中,并且您想对调用代码隐藏库的详细信息,则不一定希望调用堆栈包含有关库中私有方法的信息。在这种情况下,您可以在库的公共方法中捕获异常,然后重新抛出它们,以便调用堆栈从这些公共方法开始。
【讨论】:
Throw;:重新抛出原来的异常,保持异常类型。
Throw new exception();: 重新抛出原始异常类型并重置异常堆栈跟踪
Throw ex;:重置异常堆栈跟踪并重置异常类型
【讨论】: