捕获异常并重新抛出异常时应考虑哪些最佳实践? 我要确保保留Exception对象的InnerException和堆栈跟踪。 以下代码块之间的处理方式是否有所不同?

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

VS:

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

===============>>#1 票数:260 已采纳

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

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

throw ex; 从本质上讲,就像从该点引发异常一样,因此堆栈跟踪只会到达您发出throw ex; 声明。

迈克也是正确的,假设该异常允许您传递异常(建议)。

Karl Seguin编程电子书的基础 上也对异常处理进行了出色的撰写 ,这是一本不错的书。

编辑:工作链接到编程基础 pdf。 只需在文本中搜索“ exception”即可。

===============>>#2 票数:99

如果您使用初始异常抛出新异常,则您还将保留初始堆栈跟踪。

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

===============>>#3 票数:29

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

在我的情况下,我必须选择第一种方法,因为我使用的第3方库引发的异常未实现此构造函数。

===============>>#4 票数:20

throw ex ,实际上是在抛出新的异常,并且会错过原始堆栈跟踪信息。 throw是首选方法。

===============>>#5 票数:13

经验法则是避免捕获和抛出基本的Exception对象。 这迫使您对异常情况要更聪明。 换句话说,您应该有一个SqlException的显式捕获,以便您的处理代码不会对NullReferenceException做任何错误。

虽然在现实世界中,捕获和记录基本异常也是一种好习惯,但是不要忘了遍历整个过程以获得它可能拥有的任何InnerExceptions

===============>>#6 票数:9

您应该始终使用“投掷”; 重新抛出.NET中的异常,

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

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

  • C#的“ throw ex;” 被编译成MSIL的“抛出”
  • C#的“抛出;” -进入MSIL“重新抛出”!

基本上,我可以看到“ throw ex”覆盖堆栈跟踪的原因。

===============>>#7 票数:9

没有人能解释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将为您提供堆栈跟踪,其中CallingMethod方法的源代码行号是throw调用的行号。 这意味着,如果throw new Exception( "TEST" )行被其他操作包围,则您不知道实际在哪个行号上引发了异常。

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

===============>>#8 票数:8

一些人实际上错过了一个非常重要的观点-“ 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行,您必须使用原始异常堆栈跟踪抛出一个新异常。

===============>>#9 票数:3

我肯定会使用:

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

那将保留您的堆栈。

===============>>#10 票数:3

您也可以使用:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

并且抛出的任何异常都将上升到处理它们的下一个层次。

===============>>#11 票数:0

仅供参考,我刚刚对此进行了测试,并且“ 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()的哪些调用导致了异常。

  ask by Seibar translate from so

未解决问题?本站智能推荐: