在一个专门处理异常并保留堆栈跟踪的方法中,如何重新抛出C#异常?

3
我希望编写一个处理异常的方法,该方法将在catch块内调用。根据传递的异常类型,异常要么作为新异常的内部异常传递,要么仅重新抛出。如何在第二种情况下保留堆栈跟踪?
示例:
public void TestMethod()
{
    try
    {
        // can throw an exception specific to the project or a .Net exception 
        SomeWorkMethod() 
    }
    catch(Exception ex)
    {
        HandleException(ex);
    }
}

private void HandleException(Exception ex)
{
    if(ex is SpecificException)
         throw ex; //will not preserve stack trace...
    else
         throw new SpecificException(ex);
}

我不想做的是,因为这个模式在许多地方都被重复使用,而且没有任何因数分解:

try
{
    SomeWorkMethod();
}
catch(Exception ex)
{
    if(ex is SpecificException)
         throw;
    else
         throw new SpecificException(ex);
}

3
为什么不使用类似于 "if (!HandleException(ex)) throw;" 这样的东西呢?我知道它并不完全符合你的要求,但它很简单,不像第二个例子那么复杂。 - Julo
2
为什么你没有单独为 SpecificException 添加 catch 呢? catch(SpecificException e){throw;} - juharr
3个回答

6

在不指定异常的情况下使用throw可以保留堆栈跟踪信息。这只能在catch块内完成。您可以在不抛出原始异常的情况下从HandleException返回,然后立即使用throw

public void TestMethod()
{
    try
    {
        // can throw an exception specific to the project or a .Net exception 
        SomeWorkMethod() 
    }
    catch(Exception ex)
    {
        HandleException(ex);
        throw;
    }
}

private void HandleException(Exception ex)
{
    if(ex is SpecificException)
         return;
    else
         throw new SpecificException(ex);
}

只要您仅使用is来对异常进行分类,首选的方法是使用两个catch块:
public void TestMethod()
{
    try
    {
        // can throw an exception specific to the project or a .Net exception 
        SomeWorkMethod() 
    }
    catch (SpecificException)
    {
        throw;
    }
    catch(Exception ex)
    {
        throw new SpecificException(ex);
    }
}

使用C# 6.0,您还可以使用when让异常继续传递:

public void TestMethod()
{
    try
    {
        // can throw an exception specific to the project or a .Net exception 
        SomeWorkMethod() 
    }
    catch(Exception ex) when (!(ex is SpecificException))
    {
        throw new SpecificException(ex);
    }
}

3

实际上,在 .Net4.5 中使用 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture 有一种非常好的方法来实现这一点:

void Main()
{
    try
    {
        throw new Exception(); //appears to be thrown from here
    }
    catch(Exception ex)
    {
        ThrowEx(ex);
    }

}
public void ThrowEx(Exception ex)
{
    if(someCondition)
    {
        //we're in the scope of a `catch` and the exception
        //is "active", so the exception with original stack-trace
        //can be re-thrown with
        ExceptionDispatchInfo.Capture(ex).Throw();
        //the following line is unreachable, but the compiler doesn't realise
        //so we can "tell" the compiler that this branch ends in an exception
        //and avoid having to return anything in the non-void case
        throw new Exception(); 
    }
}

谢谢spender,你非常准确地回答了我的问题。然而,我没有注意到有很多使用catch块来实现我所需要的方法(Sefe的第二个答案)。 - Sylvain B.
@SylvainB。确实如此。被接受的答案绝对更适合你的需求。你的问题让我想起了我以前见过的东西,所以分享一下似乎很值得。 - spender

-2

或许你可以尝试这个。

if(ex.Gettype() == typeof(SpecificException))

就我所见,这并没有真正回答问题。或者至少,如果它回答了,那么它需要一些额外的解释来说明如何回答。 - Chris
这在多个方面都是错误的。为什么要使用反射?如果 ex 继承自 SpecificException 呢?为什么不使用像 is 这样的常规语言结构(OP 已经在使用了)? - Sefe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接