11

I'm working with some classes which, when throwing, have a relatively deep InnerException tree. I'd like to log and act upon the innermost exception which is the one having the real reason for the problem.

I'm currently using something similar to

public static Exception getInnermostException(Exception e) {
    while (e.InnerException != null) {
        e = e.InnerException;
    }
    return e;
}

Is this the proper way to handle Exception trees?

Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
  • Possible duplicate of [Find the inner-most exception without using a while loop?](http://stackoverflow.com/questions/3876456/find-the-inner-most-exception-without-using-a-while-loop) – DavidRR Dec 07 '15 at 18:42
  • If anything, it's the other one the duplicate, this one's older and has the right answer as accepted. – Vinko Vrsalovic Dec 08 '15 at 11:26
  • Question age isn't always the governing criterion for designating a duplicate. For example, consider that the other question has more than ten times the number of views that this one does. Also, the accepted answer reflects only the opinion of the person that asked the question. And finally, please note that the [most highly voted answer](http://stackoverflow.com/a/5792456/1497596) to the other question also offers `GetBaseException()`, but then indicates its limitations in some situations. – DavidRR Dec 08 '15 at 13:15
  • Number of views is moot when there's a duplicate link. Likely it's due to the title wording, so I edited the title. Anyway, just flag it and let the community fix this if desired. – Vinko Vrsalovic Dec 08 '15 at 13:19
  • For criteria to use for closing a duplicate, on Meta, see [this answer](http://meta.stackoverflow.com/a/255411/1497596) and [this answer](http://meta.stackoverflow.com/a/271331/1497596). And by the way, duplicate questions can be quite valuable in that they can guide people to a topic of interest who may take somewhat different paths getting to that topic of interest. And a question closed as a duplicate can still get up (or down) votes and comments. This applies to its answers as well. – DavidRR Dec 08 '15 at 13:53

4 Answers4

12

I think you can get the innermost exception using the following code:

public static Exception getInnermostException(Exception e) { 
    return e.GetBaseException(); 
}
Vinko Vrsalovic
  • 330,807
  • 53
  • 334
  • 373
Henric
  • 1,380
  • 2
  • 16
  • 30
4

You could use the GetBaseException method. Very quick example:

try
{
    try
    {
        throw new ArgumentException("Innermost exception");
    }
    catch (Exception ex)
    {
        throw new Exception("Wrapper 1",ex);
    }
}
catch (Exception ex)
{
    // Writes out the ArgumentException details
    Console.WriteLine(ex.GetBaseException().ToString());
}
AdaTheDev
  • 142,592
  • 28
  • 206
  • 200
0

In a word, yes. I cannot think of any significantly better or different way of doing it. Unless you wanted to add it as an extension method instead, but it's really six of one, half-a-dozen of the other.

Quick Joe Smith
  • 8,074
  • 3
  • 29
  • 33
0

There are exceptions that can have multiple root causes (e.g. AggregateException and ReflectionTypeLoadException).

I created my own class to navigate the tree and then different visitors to either collect everything or just the root causes. Sample outputs here. Relevant code snippet below.

public void Accept(ExceptionVisitor visitor)
{
    Read(this.exception, visitor);
}

private static void Read(Exception ex, ExceptionVisitor visitor)
{
    bool isRoot = ex.InnerException == null;
    if (isRoot)
    {
        visitor.VisitRootCause(ex);
    }

    visitor.Visit(ex);
    visitor.Depth++;

    bool isAggregateException = TestComplexExceptionType<AggregateException>(ex, visitor, aggregateException => aggregateException.InnerExceptions);
    TestComplexExceptionType<ReflectionTypeLoadException>(ex, visitor, reflectionTypeLoadException => reflectionTypeLoadException.LoaderExceptions);

    // aggregate exceptions populate the first element from InnerExceptions, so no need to revisit
    if (!isRoot && !isAggregateException)
    {
        visitor.VisitInnerException(ex.InnerException);
        Read(ex.InnerException, visitor);
    }

    // set the depth back to current context
    visitor.Depth--;
}

private static bool TestComplexExceptionType<T>(Exception ex, ExceptionVisitor visitor, Func<T, IEnumerable<Exception>> siblingEnumerator) where T : Exception
{
    var complexException = ex as T;
    if (complexException == null)
    {
        return false;
    }

    visitor.VisitComplexException(ex);

    foreach (Exception sibling in siblingEnumerator.Invoke(complexException))
    {
        visitor.VisitSiblingInnerException(sibling);
        Read(sibling, visitor);
    }

    return true;
}
kat
  • 577
  • 4
  • 7