在捕获异常并重新抛出异常时,应该考虑哪些最佳实践?我想确保Exception对象的InnerException和堆栈跟踪被保留。下面的代码块在处理这个问题的方式上有区别吗?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
try
{
//some code
}
catch
{
throw;
}
在捕获异常并重新抛出异常时,应该考虑哪些最佳实践?我想确保Exception对象的InnerException和堆栈跟踪被保留。下面的代码块在处理这个问题的方式上有区别吗?
try
{
//some code
}
catch (Exception ex)
{
throw ex;
}
Vs:
try
{
//some code
}
catch
{
throw;
}
当前回答
经验法则是避免捕获和抛出基本的Exception对象。这迫使你对异常更加聪明;换句话说,你应该有一个显式的SqlException捕捉,这样你的处理代码就不会对NullReferenceException做错误的事情。
不过,在现实世界中,捕获和记录基本异常也是一种很好的实践,但不要忘记遍历整个过程以获取可能存在的任何innerexception。
其他回答
供参考,我刚刚测试了这个,'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.
}
堆栈跟踪正确地指向异常的起源(报告的行号),但为foo()报告的行号是抛出的行;语句,因此您无法判断是哪个对bar()的调用导致了异常。
保存堆栈跟踪的方法是使用throw;这也是有效的
try {
// something that bombs here
} catch (Exception ex)
{
throw;
}
把交货;基本上就像从那个点抛出一个异常,所以堆栈跟踪只会到你发出throw ex的地方;声明。
Mike也是正确的,假设异常允许您传递一个异常(这是推荐的)。
Karl Seguin在他的编程基础电子书中也写了一篇关于异常处理的很棒的文章,非常值得一读。
编辑:编程基础的工作链接pdf。在文本中搜索“例外”即可。
如果你抛出一个带有初始异常的新异常,你也会保留初始堆栈跟踪。
try{
}
catch(Exception ex){
throw new MoreDescriptiveException("here is what was happening", ex);
}
经验法则是避免捕获和抛出基本的Exception对象。这迫使你对异常更加聪明;换句话说,你应该有一个显式的SqlException捕捉,这样你的处理代码就不会对NullReferenceException做错误的事情。
不过,在现实世界中,捕获和记录基本异常也是一种很好的实践,但不要忘记遍历整个过程以获取可能存在的任何innerexception。
实际上,在某些情况下,throw语句不会保留StackTrace信息。例如,在下面的代码中:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
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
在如上所述的情况下,有两个选项来保留原始的StackTrace:
调用异常。InternalPreserveStackTrace
因为它是一个私有方法,所以必须使用反射来调用:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
我的缺点是依赖私有方法来保存StackTrace信息。它可以在。net Framework的未来版本中进行更改。上面的代码示例和下面提出的解决方案摘自Fabrice MARGUERIE博客。
调用异常。SetObjectData
下面的技巧是由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
}
尽管它的优点是只依赖于公共方法,但它也依赖于以下异常构造函数(由第三方开发的一些异常并不实现):
protected Exception(
SerializationInfo info,
StreamingContext context
)
在我的情况下,我不得不选择第一种方法,因为我使用的第三方库引发的异常没有实现这个构造函数。