Найти самое внутреннее исключение без использования цикла while? - PullRequest
71 голосов
/ 07 октября 2010

Когда C # выдает исключение, оно может иметь внутреннее исключение.Я хочу получить самое внутреннее исключение или, другими словами, листовое исключение, которое не имеет внутреннего исключения.Я могу сделать это в цикле while:

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

Но мне было интересно, есть ли какой-нибудь однострочный текст, который я мог бы использовать для этого вместо этого.

Ответы [ 10 ]

121 голосов
/ 07 октября 2010

Oneliner:)

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

Очевидно, вы не можете сделать это проще.

Как сказано в этом ответе Гленна МакЭлхо, это единственный надежный способ.

115 голосов
/ 26 апреля 2011

Я полагаю, Exception.GetBaseException() делает то же самое, что и эти решения.

Предостережение: из различных комментариев мы выяснили, что оно не всегда буквально делает одно и то же, и в некоторых случаях рекурсивное / итеративное решение поможет вам продвинуться дальше. Это обычно самое внутреннее исключение, что, к сожалению, противоречиво, благодаря определенным типам исключений, которые переопределяют значение по умолчанию. Однако, если вы перехватываете определенные типы исключений и уверены, что они не являются странными (например, AggregateException), то я ожидаю, что оно получит законное самое внутреннее / самое раннее исключение.

15 голосов
/ 10 октября 2013

Цикл InnerExceptions - единственный надежный способ.

Если перехваченное исключение является AggregateException, то GetBaseException() возвращает только самое внутреннее AggregateException.

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

11 голосов
/ 07 октября 2010

Если вы не знаете, как глубоко вложены внутренние исключения, то нет пути обойти цикл или рекурсию.

Конечно, вы можете определить метод расширения, который абстрагирует это:

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 голосов
/ 05 марта 2013

Я знаю, что это старый пост, но я удивлен, что никто не предложил 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

1 голос
/ 20 марта 2017

Вы должны зацикливаться, и зацикливаясь, чистее будет переместить цикл в отдельную функцию.

Я создал метод расширения, чтобы справиться с этим. Он возвращает список всех внутренних исключений указанного типа, отслеживая Exception.InnerException и AggregateException.InnerExceptions.

В моей конкретной задаче погоня за внутренними исключениями была более сложной, чем обычно, потому что исключения создавались конструкторами классов, которые вызывались посредством отражения. Исключение, которое мы ловили, имело InnerException типа TargetInvocationException, и исключения, на которые мы действительно должны были смотреть, были погребены глубоко в дереве.

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")))
    {
        // ...
    }
}
1 голос
/ 05 декабря 2012

На самом деле все так просто, вы можете использовать Exception.GetBaseException()

Try
      //Your code
Catch ex As Exception
      MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try
1 голос
/ 07 октября 2010

Не совсем одна строка, но близко:

        Func<Exception, Exception> last = null;
        last = e => e.InnerException == null ? e : last(e.InnerException);
1 голос
/ 07 октября 2010

Иногда у вас может быть много внутренних исключений (много всплывающих исключений).В этом случае вы можете сделать:

List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
   es.add(e.InnerException);
   e = e.InnerException
}
1 голос
/ 07 октября 2010

Вы можете использовать рекурсию для создания метода в каком-либо служебном классе.

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