不使用while循环,找到最内层的异常?

97

在C#抛出异常时,它可能会有一个内部异常。我的目标是获取最内层的异常,或者换句话说,是没有内部异常的叶子异常。我可以使用while循环来实现:

while (e.InnerException != null)
{
    e = e.InnerException;
}

但我想知道是否有一行代码可以做到这一点。


4
我在我的一个类中抛出了一个异常,但是我的类被一个库使用,这个库会吞掉所有的异常并抛出自己的异常。问题是,库的异常非常通用,我需要知道我抛出的具体异常类型以便处理问题。更糟糕的是,库会多次嵌套地抛出它自己的异常,到任意深度。例如,它会抛出 LibraryException -> LibraryException -> LibraryException -> MyException。我的异常始终是链上的最后一个,并且没有自己的内部异常。 - Daniel T.
7
最正确的答案位于底部,目前只有2个投票——Exception.GetBaseException()。它一直存在于框架中。感谢batCattle进行了合理性检查。 - Jay
这篇文章可能对你有用:https://dev59.com/knM_5IYBdhLWcg3wQQtd#1456603。请返回翻译后的文本。 - Vahid Hassani
也许会有用 https://gist.github.com/benbrandt22/8676438 - Kiquenet
可能是Proper way to handle InnerException trees?的重复问题。 - Vinko Vrsalovic
显示剩余4条评论
12个回答

157
一个代码一行搞定的技巧 :)
while (e.InnerException != null) e = e.InnerException;

显然,你不能再简单了。

如Glenn McElhoe在这篇答案中所说,这是唯一可靠的方法。


9
GetBaseException()方法可以在不需要循环的情况下执行此操作。http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception.aspx - codingoutloud
8
这个方法对我有效,然而 Exception.GetBaseException() 对我无效。 - Tarik
这段代码不会改变 "e" 的源异常吗?我们在将 InnerException 分配给它之前,不应该先复制一份 "e" 吗? 我建议使用以下代码:var copyOfException = e; while (copyOfException.InnerException != null) copyOfException = copyOfException.InnerException; - Mohammad Reza Sadreddini

129

我相信 Exception.GetBaseException() 执行与这些解决方案相同的操作。

注意:从各种评论中,我们已经发现它并不总是字面上执行相同的操作,并且在某些情况下,递归/迭代解决方案会让你更进一步。通常它是最内部的异常,但由于某些类型的异常覆盖了默认值,这是令人失望的不一致的。但是,如果你捕获特定类型的异常并合理地确定它们不是奇怪的东西(比如 AggregateException),那么我希望它会得到合法的最内部/最早的异常。


3
了解BCL是避免复杂解决方案的最佳方式。显然,这是最正确的答案。 - Jay
4
由于某种原因,“GetBaseException()”没有返回最初的根异常。 - Tarik
4
Glenn McElhoe 指出,实际上 GetBaseException() 并不总是按照 MSDN 上所述的我们可以一般性地期望的方式运行(即“是一个或多个后续异常的根本原因的异常”)。在 AggregateException 中,它仅限于相同类型的最低异常,可能还有其他异常。 - Josh Sutterfield
1
无法解决我的问题。我比这提供的还要深入几个层次。 - Giovanni B
2
GetBaseException() 对我没有用,因为最顶层的异常是一个 AggregateException。 - Chris Ballance
显示剩余2条评论

25

1
好的发现。谢谢 - 这解释了上面的评论,其中某些人的答案无法正常工作。这使得清楚MSDN对“一个或多个后续异常的根本原因”的一般描述并不总是您所假设的,因为派生类可能会覆盖它。真正的规则似乎是在一系列异常中所有异常都“同意”GetBaseException()并返回相同的对象,这似乎是AggregateException的情况。 - Josh Sutterfield
e.GetBaseException().GetBaseException(); 是什么意思?正如这个答案中所提到的。 - Muhammad Sulaiman

13

如果您不知道内部异常的嵌套深度,那么避免循环或递归是不可能的。

当然,您可以定义一个扩展方法来抽象化这个过程:

public static class ExceptionExtensions
{
    public static Exception GetInnermostException(this Exception e)
    {
        if (e == null)
        {
            throw new ArgumentNullException("e");
        }

        while (e.InnerException != null)
        {
            e = e.InnerException;
        }

        return e;
    }
}

10

1
确实在2012年12月5日之前被提及过。 - armadillo.mx

2
您可以使用递归来创建一个实用类中的方法。
public Exception GetFirstException(Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

使用:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = GetFirstException(ex);
}

建议使用的扩展方法(@dtb提出的好主意)
public static Exception GetFirstException(this Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

使用:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = ex.GetFirstException();
}

有用的 public static Exception GetOriginalException(this Exception ex) { if (ex.InnerException == null) return ex; return ex.InnerException.GetOriginalException(); } - Kiquenet
AggregateException.InnerExceptions 不适用吗? - Kiquenet

2
有时候你可能会有很多内部异常(许多冒泡异常)。在这种情况下,您可能想要执行以下操作:
List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}

1
其实很简单,你可以使用Exception.GetBaseException()
Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try

谢谢你的解决方案!很少有人会用VB思考 :D - Federico Navarrete
1
还有一个原因! :P - Yoda

1
我遇到了这个问题,希望能够列出来自异常“堆栈”的所有异常消息。因此,我想出了以下解决方案。
public static string GetExceptionMessages(Exception ex)
{
    if (ex.InnerException is null)
        return ex.Message;
    else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
}

1

虽然不是一行代码,但很接近:

        Func<Exception, Exception> last = null;
        last = e => e.InnerException == null ? e : last(e.InnerException);

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