C#有没有办法知道是否已经捕获了异常

时间:2018-04-04 16:07:54

标签: c# .net exception

有没有办法确定先前是否已捕获(并重新抛出)异常?例如:

public void Main()
{
    try
    {
       Child();
    } 
    catch( Exception ex)
    {
    // do something only if exception was not already in another catch block
    }
}


public void Child()
{
    try
    {
        A_ThisMayThrowException();
    }
    catch (Exception ex)
    {
       LogError();
       throw;
    }

    B_ThisMayAlsoThrowAnErrorButWillNotBeCaughtHere();


}

在Main函数中,在catch块中,有没有办法确定是否已在Child函数中捕获到异常?

2 个答案:

答案 0 :(得分:4)

我会创建一个自定义LoggedException并将其抛出堆栈:

catch (Exception ex)
{
     Log(ex);
     throw new LoggedException(ex);
}

答案 1 :(得分:2)

虽然我不确定我是否建议这样做,但您可以修改Exception.Data属性返回的字典。如果您的LogError方法接受异常并以允许您稍后检查的方式对其进行修改,则该方法适用于您。

请注意,与InBetween的答案不同,这允许其余代码关心日志记录方面。例如,您可能有一些代码想要根据类型捕获异常 - 我觉得像“将记录的异常标记为已记录”的部分应与此正交。如果您正在更改抛出的异常类型,那么可以以与日志记录无关的方式更改堆栈中的代码行为。

以下是一个例子:

using System;

class Test
{
    static void Main()
    {
        TestHandling(true);
        TestHandling(false);
    }

    static void TestHandling(bool throwFirst)
    {
        try
        {
            Child(throwFirst);
        } 
        catch (Exception ex)
        {
            Console.WriteLine($"Previously caught? {ex.Data.Contains("Logged")}");
        }
    }


    static void Child(bool throwFirst)
    {
        try
        {
            if (throwFirst)
            {
                throw new Exception();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Logging!");
            ex.Data["Logged"] = true;
            throw;
        }
        throw new Exception();
    }
}

输出:

Logging!
Previously caught? True
Previously caught? False

这种方法也适用于异常过滤器,你甚至可以只记录一次。例如:

public bool LogAndThrow(Exception ex)
{
    if (!ex.Data.Contains("Logged"))
    {
        // Replace with real logging
        Console.WriteLine("An exception occurred!");
        ex.Data["Logged"] = true;
    }
    // Always continue up the stack: this never filters
    return false;
}

public static bool CatchIfNotLogged(this Exception ex) =>
    !ex.Data.Contains("Logged");

然后你可以:

try
{
    Foo();
}
catch (Exception ex) when (ex.CatchIfNotLogged()
{
    // We only get here if the exception hasn't been logged
}

try
{
    Foo();
}
catch (Exception ex) when (ex.LogAndThrow())
{
    // We never get here
}