在不使用while循环的情况下找到最内部的异常?

时间:2010-10-06 20:11:30

标签: c# .net exception exception-handling while-loop

当C#抛出异常时,它可能有一个内部异常。我想要做的是获取最内部的异常,或者换句话说,没有内部异常的叶子异常。我可以在while循环中执行此操作:

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

但我想知道是否有一些单行代码可以用来代替。

12 个答案:

答案 0 :(得分:120)

Oneliner:)

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

显然,你不能让它变得更简单。

正如Glenn McElhoe在this answer中所说,这是唯一可靠的方式。

答案 1 :(得分:115)

我相信Exception.GetBaseException()与这些解决方案完全相同。

警告:从各种评论中我们已经发现它不会总是从字面上做同样的事情,在某些情况下,递归/迭代解决方案会让你更进一步。它通常是最内层的异常,由于某些类型的异常覆盖了默认值,因此令人失望地不一致。但是,如果您捕获特定类型的异常并合理地确定它们不是古怪的(如AggregateException),那么我会期望它获得合法的最内部/最早的异常。

答案 2 :(得分:15)

通过InnerExceptions循环是唯一可靠的方法。

如果捕获的异常是AggregateException,则GetBaseException()仅返回最内层的AggregateException。

http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx

答案 3 :(得分:11)

如果您不知道嵌套内部异常有多深,则无法绕过循环或递归。

当然,您可以定义一个抽象方法来抽象出来:

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;
    }
}

答案 4 :(得分:10)

我知道这是一篇旧帖子,但我很惊讶没有人建议GetBaseException()这是Exception类的方法:

catch (Exception x)
{
    var baseException = x.GetBaseException();
}

自.NET 1.1以来,这一直存在。文档在这里: http://msdn.microsoft.com/en-us/library/system.exception.getbaseexception(v=vs.71).aspx

答案 5 :(得分:1)

您可以使用递归在某个实用程序类中创建方法。

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();
}

答案 6 :(得分:1)

有时您可能会遇到许多内部异常(许多冒泡异常)。 在这种情况下,您可能想要这样做:

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}

答案 7 :(得分:1)

不是一行而是关闭:

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

答案 8 :(得分:1)

事实上很简单,你可以使用Exception.GetBaseException()

Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try

答案 9 :(得分: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")))
    {
        // ...
    }
}

答案 10 :(得分:0)

另一种方法是调用两次GetBaseException()

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

之所以起作用,是因为如果它是AggregateException,则第一个调用会将您带到最内层的非AggregateException,然后第二个调用会将您带到该异常的最内层异常。如果第一个异常不是AggregateException,则第二个调用仅返回相同的异常。

答案 11 :(得分:0)

我遇到了这个问题,希望能够列出来自异常“堆栈”的所有异常消息。所以,我想到了这个。

public static string GetExceptionMessages(Exception ex)
{
    if (ex.InnerException is null)
        return ex.Message;
    else return $"{ex.Message}\n{GetExceptionMessages(ex.InnerException)}";
}