C# 捕获和重新抛出 .NET 异常的最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22623/
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
Best practices for catching and re-throwing .NET exceptions
提问by Seibar
What are the best practices to consider when catching exceptions and re-throwing them? I want to make sure that the Exception
object's InnerException
and stack trace are preserved. Is there a difference between the following code blocks in the way they handle this?
捕获异常并重新抛出异常时要考虑的最佳实践是什么?我想确保保留Exception
对象InnerException
和堆栈跟踪。以下代码块在处理此问题的方式上有区别吗?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
对比:
try
{
//some code
}
catch
{
throw;
}
采纳答案by Darren Kopp
The way to preserve the stack trace is through the use of the throw;
This is valid as well
保留堆栈跟踪的方法是通过使用throw;
This is valid as well
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
throw ex;
is basically like throwing an exception from that point, so the stack trace would only go to where you are issuing the throw ex;
statement.
throw ex;
基本上就像从那个点抛出异常,所以堆栈跟踪只会到你发出throw ex;
语句的地方。
Mikeis also correct, assuming the exception allows you to pass an exception (which is recommended).
Mike也是正确的,假设异常允许您传递异常(推荐)。
Karl Seguinhas a great write up on exception handlingin his foundations of programming e-bookas well, which is a great read.
卡尔塞甘有一个伟大的写了对异常处理在他的编程电子书的基础为好,这是一个伟大的阅读。
Edit: Working link to Foundations of Programmingpdf. Just search the text for "exception".
编辑:指向编程基础pdf 的工作链接。只需在文本中搜索“例外”即可。
回答by Forgotten Semicolon
When you throw ex
, you're essentially throwing a new exception, and will miss out on the original stack trace information. throw
is the preferred method.
当您 时throw ex
,您实际上是在抛出一个新异常,并且会错过原始堆栈跟踪信息。 throw
是首选方法。
回答by swilliams
The rule of thumb is to avoid Catching and Throwing the basic Exception
object. This forces you to be a little smarter about exceptions; in other words you should have an explicit catch for a SqlException
so that your handling code doesn't do something wrong with a NullReferenceException
.
经验法则是避免捕捉和投掷基本Exception
对象。这迫使您对异常更加聪明;换句话说,您应该对 a 进行显式捕获,SqlException
以便您的处理代码不会对 a做任何错误NullReferenceException
。
In the real world though, catching and loggingthe base exception is also a good practice, but don't forget to walk the whole thing to get any InnerExceptions
it might have.
但在现实世界中,捕获和记录基本异常也是一种很好的做法,但不要忘记遍历整个过程以获取InnerExceptions
可能存在的任何异常。
回答by Mike
If you throw a new exception with the initial exception you will preserve the initial stack trace too..
如果您在初始异常中抛出一个新异常,您也将保留初始堆栈跟踪。
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
回答by 1kevgriff
I would definitely use:
我肯定会使用:
try
{
//some code
}
catch
{
//you should totally do something here, but feel free to rethrow
//if you need to send the exception up the stack.
throw;
}
That will preserve your stack.
这将保留您的堆栈。
回答by Erick B
You may also use:
您还可以使用:
try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}
And any exceptions thrown will bubble up to the next level that handles them.
并且抛出的任何异常都会冒泡到处理它们的下一个级别。
回答by Vinod T. Patil
You should always use "throw;" to rethrow the exceptions in .NET,
你应该总是使用“throw;” 在 .NET 中重新抛出异常,
Refer this, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
参考这个, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx
Basically MSIL (CIL) has two instructions - "throw" and "rethrow":
基本上 MSIL (CIL) 有两条指令——“抛出”和“重新抛出”:
- C#'s "throw ex;" gets compiled into MSIL's "throw"
- C#'s "throw;" - into MSIL "rethrow"!
- C# 的 "throw ex;" 被编译成 MSIL 的“throw”
- C# 的“抛出;” - 进入MSIL“重新抛出”!
Basically I can see the reason why "throw ex" overrides the stack trace.
基本上我可以看到“throw ex”覆盖堆栈跟踪的原因。
回答by redcalx
FYI I just tested this and the stack trace reported by 'throw;' is not an entirely correct stack trace. Example:
仅供参考我刚刚测试了这个和'throw;'报告的堆栈跟踪 不是一个完全正确的堆栈跟踪。例子:
private void foo()
{
try
{
bar(3);
bar(2);
bar(1);
bar(0);
}
catch(DivideByZeroException)
{
//log message and rethrow...
throw;
}
}
private void bar(int b)
{
int a = 1;
int c = a/b; // Generate divide by zero exception.
}
The stack trace points to the origin of the exception correctly (reported line number) but the line number reported for foo() is the line of the throw; statement, hence you cannot tell which of the calls to bar() caused the exception.
堆栈跟踪正确指向异常的来源(报告的行号),但为 foo() 报告的行号是 throw 的行;语句,因此您无法判断对 bar() 的哪个调用导致了异常。
回答by notlkk
A few people actually missed a very important point - 'throw' and 'throw ex' may do the same thing but they don't give you a crucial piece of imformation which is the line where the exception happened.
有些人实际上错过了一个非常重要的点——“throw”和“throw ex”可能会做同样的事情,但他们没有给你一条关键的信息,即发生异常的那一行。
Consider the following code:
考虑以下代码:
static void Main(string[] args)
{
try
{
TestMe();
}
catch (Exception ex)
{
string ss = ex.ToString();
}
}
static void TestMe()
{
try
{
//here's some code that will generate an exception - line #17
}
catch (Exception ex)
{
//throw new ApplicationException(ex.ToString());
throw ex; // line# 22
}
}
When you do either a 'throw' or 'throw ex' you get the stack trace but the line# is going to be #22 so you can't figure out which line exactly was throwing the exception (unless you have only 1 or few lines of code in the try block). To get the expected line #17 in your exception you'll have to throw a new exception with the original exception stack trace.
当您执行 'throw' 或 'throw ex' 时,您将获得堆栈跟踪,但第 22 行将是 #22,因此您无法确定究竟是哪一行抛出异常(除非您只有 1 个或几个try 块中的代码行)。要在异常中获得预期的第 17 行,您必须使用原始异常堆栈跟踪抛出一个新异常。
回答by CARLOS LOTH
Actually, there are some situations which the throw
statment will not preserve the StackTrace information. For example, in the code below:
实际上,在某些情况下,throw
语句不会保留 StackTrace 信息。例如,在下面的代码中:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
The StackTrace will indicate that line 54 raised the exception, although it was raised at line 47.
StackTrace 将指示第 54 行引发了异常,尽管它是在第 47 行引发的。
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
In situations like the one described above, there are two options to preseve the original StackTrace:
在上述情况下,有两种选择可以保留原始 StackTrace:
Calling the Exception.InternalPreserveStackTrace
调用 Exception.InternalPreserveStackTrace
As it is a private method, it has to be invoked by using reflection:
由于它是私有方法,因此必须使用反射来调用它:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
I has a disadvantage of relying on a private method to preserve the StackTrace information. It can be changed in future versions of .NET Framework. The code example above and proposed solution below was extracted from Fabrice MARGUERIE weblog.
我的缺点是依赖私有方法来保存 StackTrace 信息。它可以在 .NET Framework 的未来版本中更改。上面的代码示例和下面提出的解决方案摘自Fabrice MARGUERIE 博客。
Calling Exception.SetObjectData
调用 Exception.SetObjectData
The technique below was suggested by Anton Tykhyyas answer to In C#, how can I rethrow InnerException without losing stack tracequestion.
Anton Tykhyy建议使用以下技术作为在 C# 中的答案,如何在不丢失堆栈跟踪问题的情况下重新抛出 InnerException。
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
Although, it has the advantage of relying in public methods only it also depends on the following exception constructor (which some exceptions developed by 3rd parties do not implement):
虽然,它的优点是只依赖于公共方法,但它也依赖于以下异常构造函数(某些 3rd 方开发的异常没有实现):
protected Exception(
SerializationInfo info,
StreamingContext context
)
In my situation, I had to choose the first approach, because the exceptions raised by a 3rd-party library I was using didn't implement this constructor.
在我的情况下,我不得不选择第一种方法,因为我使用的第 3 方库引发的异常没有实现这个构造函数。