当前位置:  开发笔记 > 编程语言 > 正文

捕获和重新抛出.NET异常的最佳实践

如何解决《捕获和重新抛出.NET异常的最佳实践》经验,为你挑选了8个好方法。

捕获异常并重新抛出异常时需要考虑哪些最佳实践?我想确保保留Exception对象InnerException和堆栈跟踪.以下代码块在处理此方式时是否存在差异?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

VS:

try
{
    //some code
}
catch
{
    throw;
}

Darren Kopp.. 260

保留堆栈跟踪的方法是通过使用throw;它也是有效的

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;基本上就像从那一点抛出一个异常,所以堆栈跟踪只会到你发出throw ex;语句的地方.

Mike也是正确的,假设异常允许您传递异常(建议使用).

Karl Seguin在编写电子书的基础上对异常处理有很好的写作,这是一本很好的读物.

编辑:编程基础的工作链接pdf.只需在文本中搜索"例外"即可.



1> Darren Kopp..:

保留堆栈跟踪的方法是通过使用throw;它也是有效的

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex;基本上就像从那一点抛出一个异常,所以堆栈跟踪只会到你发出throw ex;语句的地方.

Mike也是正确的,假设异常允许您传递异常(建议使用).

Karl Seguin在编写电子书的基础上对异常处理有很好的写作,这是一本很好的读物.

编辑:编程基础的工作链接pdf.只需在文本中搜索"例外"即可.


我不太确定这篇文章是否精彩,它表明try {// ...} catch(Exception ex){throw new Exception(ex.Message +"other stuff"); } 很好.问题是你完全无法在堆栈中进一步处理该异常,除非你捕获所有异常,一个很大的禁忌(你确定你想要处理那个OutOfMemoryException?)
有时*throw;*不足以保留堆栈跟踪.这是一个例子[https://dotnetfiddle.net/CkMFoX](https://dotnetfiddle.net/CkMFoX)
或`ExceptionDispatchInfo.Capture(ex).Throw(); .NET +4.5中的throw;`/sf/ask/17360801/#17091351
@ljs自评论以来文章发生了变化,因为我没有看到他推荐的任何部分.事实恰恰相反,他说不要这样做,并询问你是否也要处理OutOfMemoryException!?

2> Mike..:

如果您使用初始异常抛出一个新异常,那么您也将保留初始堆栈跟踪.

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}



3> CARLOS LOTH..:

实际上,在某些情况下,该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:

调用Exception.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博客中提取的.

调用Exception.SetObjectData

Anton Tykhyy建议将下面的技术作为In 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
)

在我的情况下,我不得不选择第一种方法,因为我使用的第三方库引发的异常没有实现这个构造函数.


在.NET 4.5中,还有第三个,也是我认为更干净的选项:使用ExceptionDispatchInfo。有关更多信息,请参见Tragedians在此处回答相关问题:http://stackoverflow.com/a/17091351/567000。

4> Forgotten Se..:

当你throw ex,你实际上抛出一个新的异常,并错过了原始的堆栈跟踪信息. throw是首选的方法.



5> swilliams..:

经验法则是避免捕获和抛出基本Exception对象.这迫使你对异常更聪明一点; 换句话说,你应该有一个明确的catch,SqlException以便你的处理代码不会出错NullReferenceException.

在现实世界中,捕获和记录基本异常也是一种很好的做法,但不要忘记走完整个事情以获得InnerExceptions它可能具有的任何东西.


我认为最好通过使用AppDomain.CurrentDomain.UnhandledException和Application.ThreadException异常来处理未处理的日志记录异常.使用big try {...} catch(Exception ex){...}块无处不在意味着大量的重复.取决于您是否要记录已处理的异常,在这种情况下(至少是最小的)重复可能是不可避免的.

6> Vinod T. Pat..:

你应该总是使用"扔"; 重新抛出.NET中的异常,

请参阅此处, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

基本上MSIL(CIL)有两条指令 - "throw"和"rethrow":

C#的"扔前"; 被编译成MSIL的"扔"

C#的"扔"; - 进入MSIL"重新抛出"!

基本上我可以看到"throw ex"覆盖堆栈跟踪的原因.



7> jeuoekdcwzfw..:

没有人解释ExceptionDispatchInfo.Capture( ex ).Throw()和平原之间的区别throw,所以在这里.但是,有些人已经注意到了这个问题throw.

重新抛出捕获的异常的完整方法是使用ExceptionDispatchInfo.Capture( ex ).Throw()(仅可从.Net 4.5获得).

下面是测试这个的必要案例:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

情况1和情况2将为您提供堆栈跟踪,其中方法的源代码行号CallingMethod是该行的行号throw new Exception( "TEST" ).

但是,案例3将为您提供堆栈跟踪,其中方法的源代码行号CallingMethodthrow调用的行号.这意味着如果该throw new Exception( "TEST" )行被其他操作包围,您不知道实际抛出异常的行号.

情况4与情况2类似,因为原始异常的行号被保留,但不是真正的重新抛出,因为它改变了原始异常的类型.



8> notlkk..:

一些人实际上错过了一个非常重要的观点 - 'throw'和'throw ex'可能会做同样的事情,但他们没有给你一个关键的信息,这是异常发生的线.

请考虑以下代码:

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

当你执行'throw'或'throw ex'时,你得到堆栈跟踪,但是#行将是#22,所以你无法确定究竟是哪一行抛出了异常(除非你只有1或几个) try块中的代码行).要在异常中获得预期的第17行,您必须使用原始异常堆栈跟踪抛出新的异常.

推荐阅读
保佑欣疼你的芯疼
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有