web-dev-qa-db-fra.com

Trouver l'exception la plus interne sans utiliser de boucle while?

Lorsque C # lève une exception, il peut avoir une exception interne. Ce que je veux faire, c'est obtenir l'exception la plus interne, ou en d'autres termes, l'exception leaf qui n'a pas d'exception interne. Je peux le faire dans une boucle while:

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

Mais je me demandais s'il y avait une doublure que je pourrais utiliser à la place.

75
Daniel T.

Bon mot :)

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

De toute évidence, vous ne pouvez pas faire plus simple.

Comme dit dans cette réponse par Glenn McElhoe, c'est le seul moyen fiable.

123
Draco Ater

Je crois que Exception.GetBaseException() fait la même chose que ces solutions.

Mise en garde: à partir de divers commentaires, nous avons compris que cela ne fait pas toujours littéralement la même chose, et dans certains cas, la solution récursive/itérative obtiendra vous plus loin. C'est généralement l'exception la plus interne, ce qui est malheureusement incohérent, grâce à certains types d'exceptions qui remplacent la valeur par défaut. Cependant, si vous interceptez des types spécifiques d'exceptions et assurez-vous raisonnablement qu'elles ne sont pas bizarres (comme AggregateException), je m'attendrais à ce qu'elles obtiennent l'exception légitime la plus interne/la plus ancienne.

117
Josh Sutterfield

La boucle via InnerExceptions est le seul moyen fiable.

Si l'exception interceptée est une AggregateException, alors GetBaseException() renvoie uniquement l'exception AggregateException la plus interne.

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

17
Glenn McElhoe

Si vous ne savez pas à quelle profondeur les exceptions internes sont imbriquées, il n'y a aucun moyen de contourner une boucle ou une récursivité.

Bien sûr, vous pouvez définir une méthode d'extension qui résume cela:

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

Je sais que c'est un ancien article, mais je suis surpris que personne n'ait suggéré GetBaseException() qui est une méthode sur la classe Exception:

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

Cela existe depuis .NET 1.1. Documentation ici: http://msdn.Microsoft.com/en-us/library/system.exception.getbaseexception (v = vs.71) .aspx

10
Matt

Parfois, vous pouvez avoir de nombreuses exceptions internes (de nombreuses exceptions bouillonnées). Dans ce cas, vous voudrez peut-être faire:

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

En fait, c'est si simple, vous pouvez utiliser Exception.GetBaseException()

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

Pas tout à fait une ligne mais proche:

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

Vous pouvez utiliser la récursivité pour créer une méthode dans une classe utilitaire quelque part.

public Exception GetFirstException(Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Utilisation:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = GetFirstException(ex);
}

La méthode d'extension suggérée (bonne idée @dtb)

public static Exception GetFirstException(this Exception ex)
{
    if(ex.InnerException == null) { return ex; } // end case
    else { return GetFirstException(ex.InnerException); } // recurse
}

Utilisation:

try
{
    // some code here
}
catch (Exception ex)
{
    Exception baseException = ex.GetFirstException();
}
1
Brad

Vous devez boucler et avoir à boucler, il est plus propre de déplacer la boucle dans une fonction distincte.

J'ai créé une méthode d'extension pour gérer cela. Il renvoie une liste de toutes les exceptions internes du type spécifié, pourchassant Exception.InnerException et AggregateException.InnerExceptions.

Dans mon problème particulier, pourchasser les exceptions internes était plus compliqué que d'habitude, car les exceptions étaient levées par les constructeurs de classes qui étaient invoquées par la réflexion. L'exception que nous interceptions avait une InnerException de type TargetInvocationException, et les exceptions que nous devions réellement examiner étaient enfouies profondément dans l'arborescence.

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

L'utilisation est assez simple. Si, par exemple, vous voulez savoir si nous avons rencontré un

catch (Exception ex)
{
    var myExes = ex.innerExceptions<MyException>();
    if (myExes.Any(x => x.Message.StartsWith("Encountered my specific error")))
    {
        // ...
    }
}
1
Jeff Dege