Ответ 1
Oneliner:)
while (e.InnerException != null) e = e.InnerException;
Очевидно, вы не можете сделать это проще.
Как сказано в этом ответе от Glenn McElhoe, это единственный надежный способ.
Когда С# выдает исключение, оно может иметь внутреннее исключение. То, что я хочу сделать, это получить внутреннее исключение, или, другими словами, исключение листа, которое не имеет внутреннего исключения. Я могу сделать это в цикле while:
while (e.InnerException != null)
{
e = e.InnerException;
}
Но мне было интересно, есть ли какой-то один-лайнер, который я мог бы использовать для этого.
Oneliner:)
while (e.InnerException != null) e = e.InnerException;
Очевидно, вы не можете сделать это проще.
Как сказано в этом ответе от Glenn McElhoe, это единственный надежный способ.
Я полагаю, что Exception.GetBaseException()
делает то же самое, что и эти решения.
Предостережение: из различных комментариев мы выяснили, что это не всегда буквально делает одно и то же, и в некоторых случаях рекурсивное/итеративное решение поможет вам продвинуться дальше. Обычно это самое внутреннее исключение, которое, к сожалению, противоречиво, благодаря определенным типам исключений, которые переопределяют значение по умолчанию. Однако, если вы ловите определенные типы исключений и достаточно уверенно убедитесь, что они не являются странными (например, AggregateException), то я ожидаю, что оно получит законное самое внутреннее/самое раннее исключение.
Циклирование через InnerExceptions является единственным надежным способом.
Если пойманное исключение представляет собой исключение AggregateException, то GetBaseException()
возвращает только самое внутреннее исключение AggregateException.
http://msdn.microsoft.com/en-us/library/system.aggregateexception.getbaseexception.aspx
Если вы не знаете, насколько глубокие внутренние исключения вложены, нет никакого пути вокруг цикла или рекурсии.
Конечно, вы можете определить метод расширения, который абстрагирует это:
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;
}
}
Я знаю, что это старый пост, но я удивлен, что никто не предложил 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
Вы можете использовать рекурсию для создания метода в каком-либо служебном классе.
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();
}
Иногда у вас может быть много внутренних исключений (много исключений из пузырьков). В этом случае вы можете сделать:
List<Exception> es = new List<Exception>();
while(e.InnerException != null)
{
es.add(e.InnerException);
e = e.InnerException
}
Не совсем одна строка, но близко:
Func<Exception, Exception> last = null;
last = e => e.InnerException == null ? e : last(e.InnerException);
На самом деле это так просто, вы можете использовать Exception.GetBaseException()
Try
//Your code
Catch ex As Exception
MessageBox.Show(ex.GetBaseException().Message, My.Settings.MsgBoxTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
End Try
Вам нужно зациклиться, и для этого нужно очистить его, чтобы переместить цикл в отдельную функцию.
Я создал метод расширения, чтобы справиться с этим. Он возвращает список всех внутренних исключений указанного типа, преследуя исключение 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")))
{
// ...
}
}
Другой способ сделать это - дважды позвонить GetBaseException()
:
Exception innermostException = e.GetBaseException().GetBaseException();
Это работает, потому что если это AggregateException
, первый вызов возвращает вас к самому внутреннему non- AggregateException
, а второй вызов возвращает вас к самому внутреннему исключению этого исключения. Если первое исключение не является AggregateException
, то второй вызов просто возвращает то же исключение.