Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 捕获和重新抛出.NET异常的最佳实践 当捕捉异常并重新抛出它们时,最好的做法是什么?我想确保异常对象的内部异常和堆栈跟踪被保留。以下代码块在处理此问题的方式上是否存在差异 try { //some code } catch (Exception ex) { throw ex; }_C#_.net_Exception Handling_Rethrow - Fatal编程技术网

C# 捕获和重新抛出.NET异常的最佳实践 当捕捉异常并重新抛出它们时,最好的做法是什么?我想确保异常对象的内部异常和堆栈跟踪被保留。以下代码块在处理此问题的方式上是否存在差异 try { //some code } catch (Exception ex) { throw ex; }

C# 捕获和重新抛出.NET异常的最佳实践 当捕捉异常并重新抛出它们时,最好的做法是什么?我想确保异常对象的内部异常和堆栈跟踪被保留。以下代码块在处理此问题的方式上是否存在差异 try { //some code } catch (Exception ex) { throw ex; },c#,.net,exception-handling,rethrow,C#,.net,Exception Handling,Rethrow,Vs: 保存堆栈跟踪的方法是通过使用抛出这也是有效的 try { // something that bombs here } catch (Exception ex) { throw; } throw-ex基本上类似于从该点抛出异常,因此堆栈跟踪只会到达发出throw ex的位置语句 也正确,假设异常允许您传递异常(建议这样做) 他也有一本书,这是一本很棒的书 编辑:工作链接到pdf。只需在文本中搜索“异常”。当您抛出ex时,您实际上是在抛出一个新异常,并且将丢失原始堆栈跟踪信息

Vs:


保存堆栈跟踪的方法是通过使用
抛出这也是有效的

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}
throw-ex基本上类似于从该点抛出异常,因此堆栈跟踪只会到达发出
throw ex的位置语句

也正确,假设异常允许您传递异常(建议这样做)

他也有一本书,这是一本很棒的书


编辑:工作链接到pdf。只需在文本中搜索“异常”。

当您抛出ex
时,您实际上是在抛出一个新异常,并且将丢失原始堆栈跟踪信息<代码>抛出是首选方法。

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


但在现实世界中,捕获和记录基本异常也是一种很好的做法,但不要忘记了解整个过程,以获取它可能具有的任何
内部异常。

如果您在初始异常中抛出一个新异常,您也将保留初始堆栈跟踪

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

我肯定会使用:

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;
}
这将保留您的堆栈。

您还可以使用:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}
任何抛出的异常都将冒泡到下一个处理它们的级别。

您应该始终使用“throw;”在.NET中重新抛出异常

参考这个,,

基本上,MSIL(CIL)有两条指令——“抛出”和“重新抛出”:

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

基本上,我可以理解为什么“throw ex”会覆盖堆栈跟踪。

仅供参考,我刚刚测试了这一点,以及由“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”和“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,因此您无法确定哪一行确实引发了异常(除非try块中只有1行或几行代码)。要获得异常中的预期行#17,您必须使用原始异常堆栈跟踪抛出一个新异常。

实际上,在某些情况下,
throw
station不会保留堆栈跟踪信息。例如,在下面的代码中:

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的未来版本中更改。上面的代码示例和下面建议的解决方案是从中提取的

调用异常。SetObjectData

下面的技巧是作为问题的答案而提出的

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 
} 
尽管它的优点是只依赖于公共方法,但它也依赖于以下异常构造函数(第三方开发的某些异常不实现):


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

没有人解释
ExceptionDispatchInfo.Capture(ex).Throw()
和普通的
Throw
之间的区别,就是这样。然而,一些人已经注意到了
throw
的问题

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

以下是测试此功能所需的案例:

一,

二,

三,

四,

案例1和案例2将为您提供一个堆栈跟踪,其中
CallingMethod
方法的源代码行号是
throw new Exception(“TEST”)
行的行号

但是,案例3将为您提供一个堆栈跟踪,其中
CallingMethod
方法的源代码行号是
throw
调用的行号。这意味着,如果
抛出新异常(“TEST”)
行被其他操作包围,则您不知道异常实际抛出的行号


案例4与案例2类似,因为原始异常的行号被保留,但不是真正的重新显示,因为它更改了原始异常的类型。

我不太确定这篇文章是否精彩,它建议尝试{//…}catch(exception ex){throw new exception(ex.Message+“other stuff”);}很好。问题是,除非捕获所有异常,否则您完全无法进一步处理该异常(您确定要这样做吗
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
)
void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}
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.
    }
}
void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}
void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}