不使用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个回答

1
你需要循环,而且要循环的话,把循环放到一个单独的函数中会更加清晰。
我创建了一个扩展方法来处理这个问题。它返回指定类型的所有内部异常列表,追踪Exception.InnerException和AggregateException.InnerExceptions。
在我的特定问题中,追踪内部异常比通常更加复杂,因为异常是由通过反射调用的类的构造函数引发的。我们捕获的异常有一个TargetInvocationException类型的InnerException,我们实际上需要查看的异常被深深地埋藏在树中。
public static class ExceptionExtensions
{
    public static IEnumerable<T> innerExceptions<T>(this Exception ex)
        where T : Exception
    {
        var rVal = new List<T>();

        Action<Exception> lambda = null;
        lambda = (x) =>
        {
            var xt = x as T;
            if (xt != null)
                rVal.Add(xt);

            if (x.InnerException != null)
                lambda(x.InnerException);

            var ax = x as AggregateException;
            if (ax != null)
            {
                foreach (var aix in ax.InnerExceptions)
                    lambda(aix);
            }
        };

        lambda(ex);

        return rVal;
    }
}

使用方法相当简单。例如,如果您想知道我们是否遇到了一个

标记,则可以执行以下操作:
catch (Exception ex)
{
    var myExes = ex.innerExceptions<MyException>();
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
    {
        // ...
    }
}

Exception.GetBaseException() 是什么意思? - Kiquenet

0

你可以通过两次调用 GetBaseException() 来实现另一种方式:

Exception innermostException = e.GetBaseException().GetBaseException();

这段代码之所以有效,是因为如果它是一个AggregateException,第一次调用会将您带到最内层的非AggregateException,然后第二次调用会将您带到该异常的最内层异常。如果第一个异常不是AggregateException,那么第二次调用只会返回相同的异常。

AggregateException没有合适的最内层异常,因为有多个“根本原因”异常(通常来自于聚合等待的单独任务)。AggregateException.GetBaseException()返回的是AggregateException本身,而不是底层异常。实际上,它自己的InnerException仅仅是AggregateException.InnerExceptions.First()。你能做的最好的事情就像这样:AggregateException.InnerExceptions.SelectMany(innerEx => innerEx.GetBaseException()),以获取每个基本异常。他们都是平起平坐的。 - Josh Sutterfield

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