为什么在 C# 中捕获并重新抛出异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/881473/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why catch and rethrow an exception in C#?
提问by corlettk
I'm looking at the article C# - Data Transfer Objecton serializable DTOs.
我正在查看文章C# - 可序列化 DTO 上的数据传输对象。
The article includes this piece of code:
文章包含这段代码:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
The rest of the article looks sane and reasonable (to a noob), but that try-catch-throw throws a WtfException... Isn't this exactly equivalent to not handling exceptions at all?
文章的其余部分看起来很理智和合理(对菜鸟来说),但是 try-catch-throw 抛出一个 WtfException ......这不完全等同于根本不处理异常吗?
Ergo:
因此:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
Or am I missing something fundamental about error handling in C#? It's pretty much the same as Java (minus checked exceptions), isn't it? ... That is, they both refined C++.
或者我是否缺少有关 C# 中错误处理的一些基本知识?它与 Java 几乎相同(减去检查异常),不是吗?...也就是说,他们都精炼了 C++。
The Stack Overflow question The difference between re-throwing parameter-less catch and not doing anything?seems to support my contention that try-catch-throw is-a no-op.
Stack Overflow 问题重新抛出无参数捕获和什么都不做的区别?似乎支持我的论点,即 try-catch-throw 是空操作。
EDIT:
编辑:
Just to summarise for anyone who finds this thread in future...
只是为将来发现此线程的任何人总结......
DO NOT
不要
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
The stack trace information can be crucial to identifying the root cause of the problem!
堆栈跟踪信息对于确定问题的根本原因至关重要!
DO
做
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
Catch the more specific exceptions before the less specific ones (just like Java).
在不太具体的异常之前捕获更具体的异常(就像 Java 一样)。
References:
参考:
采纳答案by Fredrik M?rk
First; the way that the code in the article does it is evil. throw ex
will reset the call stack in the exception to the point where this throw statement is; losing the information about where the exception actually was created.
第一的; 文章中的代码的做法是邪恶的。throw ex
将异常中的调用堆栈重置到此 throw 语句所在的点;丢失有关实际创建异常的位置的信息。
Second, if you just catch and re-throw like that, I see no added value, the code example above would be just as good (or, given the throw ex
bit, even better) without the try-catch.
其次,如果你只是像这样捕获并重新抛出,我看不到任何附加值,上面的代码示例在throw ex
没有 try-catch 的情况下也一样好(或者,给定一点,甚至更好)。
However, there are cases where you might want to catch and rethrow an exception. Logging could be one of them:
但是,在某些情况下,您可能希望捕获并重新抛出异常。日志记录可能是其中之一:
try
{
// code that may throw exceptions
}
catch(Exception ex)
{
// add error logging here
throw;
}
回答by Duncan
You don't want to throw ex - as this will lose the call stack. See Exception Handling(MSDN).
您不想抛出 ex - 因为这会丢失调用堆栈。请参阅异常处理(MSDN)。
And yes, the try...catch is doing nothing useful (apart from lose the call stack - so it's actually worse - unless for some reason you didn't want to expose this information).
是的, try...catch 没有做任何有用的事情(除了丢失调用堆栈 - 所以它实际上更糟 - 除非出于某种原因你不想公开这些信息)。
回答by edosoft
A valid reason for rethrowing exceptions can be that you want to add information to the exception, or perhaps wrap the original exception in one of your own making:
重新抛出异常的一个有效原因可能是您想向异常添加信息,或者可能将原始异常包装在您自己制作的一个中:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
string message =
String.Format("Something went wrong serializing DTO {0}", DTO);
throw new MyLibraryException(message, ex);
}
}
回答by Eoin Campbell
Don't do this,
不要这样做,
try
{
...
}
catch(Exception ex)
{
throw ex;
}
You'll lose the stack trace information...
您将丢失堆栈跟踪信息...
Either do,
要么做,
try { ... }
catch { throw; }
OR
或者
try { ... }
catch (Exception ex)
{
throw new Exception("My Custom Error Message", ex);
}
One of the reason you might want to rethrow is if you're handling different exceptions, for e.g.
您可能想要重新抛出的原因之一是,如果您正在处理不同的异常,例如
try
{
...
}
catch(SQLException sex)
{
//Do Custom Logging
//Don't throw exception - swallow it here
}
catch(OtherException oex)
{
//Do something else
throw new WrappedException("Other Exception occured");
}
catch
{
System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
throw; //Chuck everything else back up the stack
}
回答by Pondidum
It depends what you are doing in the catch block, and if you are wanting to pass the error on to the calling code or not.
这取决于您在 catch 块中做什么,以及您是否想将错误传递给调用代码。
You might say Catch io.FileNotFoundExeption ex
and then use an alternative file path or some such, but still throw the error on.
您可能会说Catch io.FileNotFoundExeption ex
然后使用替代文件路径或其他文件路径,但仍然会抛出错误。
Also doing Throw
instead of Throw Ex
allows you to keep the full stack trace. Throw ex restarts the stack trace from the throw statement (I hope that makes sense).
也做Throw
而不是Throw Ex
允许您保留完整的堆栈跟踪。Throw ex 从 throw 语句重新开始堆栈跟踪(我希望这是有道理的)。
回答by Arjan Einbu
Isn't this exactly equivalent to not handling exceptions at all?
这不完全等同于根本不处理异常吗?
Not exactly, it isn't the same. It resets the exception's stacktrace. Though I agree that this probably is a mistake, and thus an example of bad code.
不完全一样,不一样。它重置异常的堆栈跟踪。虽然我同意这可能是一个错误,因此是一个错误代码的例子。
回答by Sheff
In the example in the code you have posted there is, in fact, no point in catching the exception as there is nothing done on the catch it is just re-thown, in fact it does more harm than good as the call stack is lost.
在您发布的代码示例中,实际上,捕获异常没有意义,因为在捕获时没有做任何事情,它只是重新抛出,实际上它弊大于利,因为调用堆栈丢失.
You would, however catch an exception to do some logic (for example closing sql connection of file lock, or just some logging) in the event of an exception the throw it back to the calling code to deal with. This would be more common in a business layer than front end code as you may want the coder implementing your business layer to handle the exception.
但是,您会在发生异常时捕获异常以执行某些逻辑(例如关闭文件锁的 sql 连接,或者只是一些日志记录),然后将其抛回调用代码进行处理。这在业务层中比前端代码更常见,因为您可能希望实现业务层的编码器处理异常。
To re-iterate though the There is NO point in catching the exception in the example you posted. DON'T do it like that!
重新迭代虽然在您发布的示例中捕获异常没有意义。不要那样做!
回答by bzlm
C# (before C# 6) doesn't support CIL "filtered exceptions", which VB does, so in C# 1-5 one reason for re-throwing an exception is that you don't have enough information at the time of catch() to determine whether you wanted to actually catch the exception.
C#(在 C# 6 之前)不支持 CIL“过滤异常”,而 VB 支持,因此在 C# 1-5 中重新抛出异常的一个原因是您在 catch() 时没有足够的信息以确定您是否要实际捕获异常。
For example, in VB you can do
例如,在VB中你可以做
Try
..
Catch Ex As MyException When Ex.ErrorCode = 123
..
End Try
...which would not handle MyExceptions with different ErrorCode values. In C# prior to v6, you would have to catch and re-throw the MyException if the ErrorCode was not 123:
...它不会处理具有不同 ErrorCode 值的 MyExceptions。在 v6 之前的 C# 中,如果 ErrorCode 不是 123,则必须捕获并重新抛出 MyException:
try
{
...
}
catch(MyException ex)
{
if (ex.ErrorCode != 123) throw;
...
}
Since C# 6.0 you can filterjust like with VB:
从 C# 6.0 开始,您可以像使用 VB 一样进行过滤:
try
{
// Do stuff
}
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
// Handle, other exceptions will be left alone and bubble up
}
回答by Brian
One possible reason to catch-throw is to disable any exception filters deeper up the stack from filtering down (random old link). But of course, if that was the intention, there would be a comment there saying so.
catch-throw 的一个可能原因是禁用堆栈更深的任何异常过滤器进行过滤(随机旧链接)。但是,当然,如果这是意图,那里会有评论这样说。
回答by Brian
Sorry, but many examples as "improved design" still smell horribly or can be extremely misleading. Having try { } catch { log; throw } is just utterly pointless. Exception logging should be done in central place inside the application. exceptions bubble up the stacktrace anyway, why not log them somewhere up and close to the borders of the system?
抱歉,但许多“改进设计”的例子仍然闻起来很可怕,或者可能会极具误导性。尝试 { } catch { 日志;throw } 完全没有意义。异常日志记录应该在应用程序内部的中心位置完成。无论如何,异常会在堆栈跟踪中冒泡,为什么不将它们记录在靠近系统边界的某个地方?
Caution should be used when you serialize your context (i.e. DTO in one given example) just into the log message. It can easily contain sensitive information one might not want to reach the hands of all the people who can access the log files. And if you don't add any new information to the exception, I really don't see the point of exception wrapping. Good old Java has some point for that, it requires caller to know what kind of exceptions one should expect then calling the code. Since you don't have this in .NET, wrapping doesn't do any good on at least 80% of the cases I've seen.
当您将上下文(即一个给定示例中的 DTO)序列化到日志消息中时,应谨慎使用。它很容易包含敏感信息,人们可能不想让所有可以访问日志文件的人都掌握它。如果您不向异常添加任何新信息,我真的看不到异常包装的意义。好的老 Java 对此有一定的意义,它要求调用者知道在调用代码时应该期待什么样的异常。由于您在 .NET 中没有这个,因此包装在我见过的至少 80% 的情况下没有任何好处。