在 C# 的 try-finally 中如何捕获原始异常

3

我的简单例子是:

    void FixedUnalterableMethod()
    {
        try
        {
            throw new Exception("Exception 1"); //line 12.
        }
        finally
        {
            throw new Exception("Exception 2"); //line 16.
        }
    }

    void Method1()
    {
        try
        {
            FixedUnalterableMethod(); //line 24.
        }
        catch (Exception ex)
        {
            var messageWithStackTrace = ex.ToString(); //line 28.
            Console.WriteLine(messageWithStackTrace);
        }
    }

控制台输出如下:
System.Exception: Exception 2
    at Program.FixedUnalterableMethod() in ...\Program.cs:line 16
    at Program.Main(String[] args) in ...\Program.cs:line 24

问题是如何得知 Exception 1 已发生? 是否有一种方法可以在我的 StackTrace 中包含 Exception 1 (位于第28行) ? 当然我无法修改 FixedUnalterableMethod() 方法!

3
不行,你不能这样做。当第二个异常被抛出时,第一个异常已经被忽略了。 - iakobski
为什么你想在 finally 中抛出一个异常?这似乎很奇怪,因为你不应该有一个总是失败的方法。 - ProgrammingLlama
@John,“FixedUnalterableMethod”方法在外部dll中,我无法更改它。但是我想知道“异常1”已经发生了。 - Cyrus
3个回答

12

是的,虽然有点不好看,但这是可能的!

很少人知道CLR异常在实际捕获异常之前不会执行finally块。这有点掩盖了一些东西,因为如果一个异常没有被捕获(并且从Main中退出),那么CLR主机代码的默认行为是为您运行finally块,给人以它们总是运行的错觉。

但是,有一种方法可以在捕获异常之前检查异常,以决定是否要捕获它。尝试这个:

static bool StoreFirstException(Exception x, Action<Exception> store)
{
    if (x.Message == "Exception 1")
    {
        store(x);                
    }

    return true;
}

static void Method1()
{
    Exception firstException = null;

    try
    {
        FixedUnalterableMethod(); //line 24.
    }
    catch (Exception ex) when (StoreFirstException(ex, x => firstException = x))
    {
        Console.WriteLine(firstException);               
        Console.WriteLine(ex);
    }
}

catch... when功能允许您编写布尔表达式来检查异常。在这里,我检查消息(您给我的唯一区分事实)并且如果它是第一个异常,则将其传递给store操作。

调用者使用此回调来存储第一个异常。

然后它投票捕获异常,这才导致finally块执行,抛出第二个异常。相同的when子句对其进行检查,但这次不会将其提供给store。然后在catch块中有两个异常,并将它们都记录下来。我的控制台显示了具有正确源代码行号的两个异常。

这是一个不查看消息的版本;它只是假设它看到的第一个异常必须是有趣的异常。此外,使用嵌套函数更加整洁:

static void Method1()
{
    Exception firstException = null;

    bool StoreFirstException(Exception x)
    {
        if (firstException == null) firstException = x;
        return true;
    }

    try
    {
        FixedUnalterableMethod(); //line 24.
    }
    catch (Exception ex) when (StoreFirstException(ex))
    {
        Console.WriteLine(firstException);               
        Console.WriteLine(ex);
    }
}

3
太棒了!这是一个可爱而有趣的小知识宝藏,虽然几乎总是无用的。 - canton7
1
非常有趣,总是很惊奇地发现这样的语言特性。感谢详细的解释! - Eric S

0

如果“异常类型”确实相同,您可能别无选择,只能检查Message属性,这可能会带来问题。

再次查看该代码,您将始终只看到一个异常,即第16行的异常。


0

感谢 @Daniel Earwicker 的帮助,工作的解决方案如下:

void FixedUnalterableMethod()
{
    try
    {
        throw new Exception("Exception 1"); //line 12.
    }
    finally
    {
        throw new Exception("Exception 2"); //line 16.
    }
}

void Method1()
{
    bool CatchException(Exception ex)
    {
        //Log...

        Console.WriteLine(ex.ToString());
        return true;
    }

    try
    {
        FixedUnalterableMethod(); //line 24.
    }
    catch (Exception ex) when (CatchException(ex))
    {
        //do something with the lastest exception
    }
}

2
传统上,在这个时候,您会接受我的答案!不需要再发布一份副本。 :) - Daniel Earwicker

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