Question

Quelles sont les meilleures pratiques à prendre en considération lors de la capture d'exceptions et de re-jeter?Je veux faire en sorte que le Exception de l'objet InnerException et la trace de la pile sont conservés.Est-il une différence entre le code suivant blocs dans leur façon de gérer cela?

try
{
    //some code
}
catch (Exception ex)
{
    throw ex;
}

Vs:

try
{
    //some code
}
catch
{
    throw;
}
Était-ce utile?

La solution

Le moyen de conserver la trace de la pile est grâce à l'utilisation de la throw; Ceci est valable aussi bien

try {
  // something that bombs here
} catch (Exception ex)
{
    throw;
}

throw ex; c'est un peu comme lancer une exception de ce point, de sorte que la trace de la pile ne voulaient pas aller à l'endroit où vous êtes à la délivrance de la throw ex; l'énoncé.

Mike est également correct, en supposant que l'exception vous permet de passer d'une exception (ce qui est recommandé).

Karl Seguin a un grande écriture sur la gestion des exceptions dans son bases de la programmation e-book ainsi, ce qui est une excellente lecture.

Edit:Lien de travail de Bases de la Programmation pdf.Il suffit de chercher le texte "exception".

Autres conseils

Si vous lancez une nouvelle exception à l'exception initiale vous permettra de conserver la trace de pile initiale de trop..

try{
} 
catch(Exception ex){
     throw new MoreDescriptiveException("here is what was happening", ex);
}

En fait, il y a certaines situations qui le throw de tresorerie ne permet pas de conserver la StackTrace de l'information.Par exemple, dans le code ci-dessous:

try
{
  int i = 0;
  int j = 12 / i; // Line 47
  int k = j + 1;
}
catch
{
  // do something
  // ...
  throw; // Line 54
}

La StackTrace indique que la ligne 54 soulevé l'exception, bien qu'il ait été soulevée à la ligne 47.

Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Program.WithThrowIncomplete() in Program.cs:line 54
   at Program.Main(String[] args) in Program.cs:line 106

Dans des situations comme celle décrite ci-dessus, il existe deux options pour preseve l'original StackTrace:

L'appel de l'Exception.InternalPreserveStackTrace

Comme c'est une méthode privée, il doit être invoquée par l'aide de la réflexion:

private static void PreserveStackTrace(Exception exception)
{
  MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
    BindingFlags.Instance | BindingFlags.NonPublic);
  preserveStackTrace.Invoke(exception, null);
}

Je possède un désavantage de s'appuyer sur une méthode privée à préserver la StackTrace de l'information.Il peut être modifié dans les prochaines versions de .NET Framework.L'exemple de code ci-dessus et la solution proposée ci-dessous a été extrait à partir de Fabrice MARGUERIE blog.

L'Appel D'Exception.SetObjectData

La technique ci-dessous a été suggéré par Anton Tykhyy comme réponse à En C#, comment puis-je renvoyer InnerException sans perdre la trace de la pile question.

static void PreserveStackTrace (Exception e) 
{ 
  var ctx = new StreamingContext  (StreamingContextStates.CrossAppDomain) ; 
  var mgr = new ObjectManager     (null, ctx) ; 
  var si  = new SerializationInfo (e.GetType (), new FormatterConverter ()) ; 

  e.GetObjectData    (si, ctx)  ; 
  mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData 
  mgr.DoFixups       ()         ; // ObjectManager calls SetObjectData 

  // voila, e is unmodified save for _remoteStackTraceString 
} 

Bien que, il a l'avantage de compter dans les méthodes publiques seulement il dépend aussi de l'exception suivante constructeur (dont certaines exceptions développé par le 3e parties ne parviennent pas à mettre en œuvre):

protected Exception(
    SerializationInfo info,
    StreamingContext context
)

Dans ma situation, j'ai dû choisir la première approche, car les exceptions soulevées par une 3ème partie de la bibliothèque je l'aide a été de ne pas mettre en œuvre ce constructeur.

Lorsque vous throw ex, vous êtes essentiellement de lancer une nouvelle exception, et ne sont pas l'original de la trace de la pile de l'information. throw est la méthode préférée.

La règle de base est d'éviter d'Attraper et de Lancer la base Exception objet.Cela vous oblige à être un peu plus intelligent sur les exceptions;en d'autres mots, il doit être explicitement prise pour SqlException afin que votre code de gestion de ne pas faire quelque chose de mal avec une NullReferenceException.

Dans le monde réel, cependant, la capture de et journalisation la base de l'exception est également une bonne pratique, mais n'oubliez pas de marcher le tout pour obtenir de l' InnerExceptions il pourrait avoir.

Personne n'a expliqué la différence entre ExceptionDispatchInfo.Capture( ex ).Throw() et un simple throw, si elle est ici.Cependant, certaines personnes ont remarqué le problème avec throw.

Le moyen de renvoyer une exception interceptée est d'utiliser ExceptionDispatchInfo.Capture( ex ).Throw() (disponible uniquement à partir de .Net 4.5).

Ci-dessous il y a les cas nécessaire de tester ceci:

1.

void CallingMethod()
{
    //try
    {
        throw new Exception( "TEST" );
    }
    //catch
    {
    //    throw;
    }
}

2.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        ExceptionDispatchInfo.Capture( ex ).Throw();
        throw; // So the compiler doesn't complain about methods which don't either return or throw.
    }
}

3.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch
    {
        throw;
    }
}

4.

void CallingMethod()
{
    try
    {
        throw new Exception( "TEST" );
    }
    catch( Exception ex )
    {
        throw new Exception( "RETHROW", ex );
    }
}

Le cas 1 et le cas 2 vous donnera une trace de la pile d'où le code source numéro de ligne de la CallingMethod la méthode est le numéro de ligne de la throw new Exception( "TEST" ) ligne.

Toutefois, des cas d'3 vous donnera une trace de la pile d'où le code source numéro de ligne de la CallingMethod la méthode est le numéro de ligne de la throw appel.Cela signifie que si l' throw new Exception( "TEST" ) ligne est entouré par d'autres activités, vous n'avez aucune idée, à qui le numéro de ligne de l'exception a été générée.

Le cas 4 est similaire avec le cas 2, car le numéro de ligne de l'exception d'origine est conservé, mais n'est pas un vrai renvoyer parce qu'il change le type de l'exception d'origine.

Quelques personnes réellement manqué un point très important - "jeter" et de "jeter de l'ex" peut faire la même chose, mais ils ne vous donnent pas un élément crucial de la imformation qui est la ligne où l'exception s'est produite.

Considérons le code suivant:

static void Main(string[] args)
{
    try
    {
        TestMe();
    }
    catch (Exception ex)
    {
        string ss = ex.ToString();
    }
}

static void TestMe()
{
    try
    {
        //here's some code that will generate an exception - line #17
    }
    catch (Exception ex)
    {
        //throw new ApplicationException(ex.ToString());
        throw ex; // line# 22
    }
}

Lorsque vous faites un "jeter" ou "jeter ex", vous obtenez la trace de la pile, mais la ligne# va #22 de sorte que vous ne pouvez pas comprendre quelle ligne exactement jetait l'exception (sauf si vous avez seulement 1 ou quelques lignes de code dans le bloc try).Pour obtenir les attendus de la ligne #17 dans votre exception, vous aurez à lancer une nouvelle exception à l'exception d'origine trace de la pile.

Vous devriez toujours utiliser "jeter;" renvoyer les exceptions .NET,

Reportez-vous cela, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

Fondamentalement MSIL (CIL) a deux instructions - "jeter" et "renvoyer":

  • C#'s "throw ex;" sera compilé en MSIL de "jeter"
  • C#'s "jeter;" - en MSIL "renvoyer"!

Fondamentalement, je peux voir la raison pour laquelle le "jeter de l'ex" remplace la trace de la pile.

Vous pouvez aussi utiliser:

try
{
// Dangerous code
}
finally
{
// clean up, or do nothing
}

Et toutes les exceptions levées vont remonter vers le niveau suivant que les gère.

Je serais certainement utiliser:

try
{
    //some code
}
catch
{
    //you should totally do something here, but feel free to rethrow
    //if you need to send the exception up the stack.
    throw;
}

Qui permettra de préserver votre pile.

Pour info je viens de tester cette et la trace de la pile rapporté par 'jeter;' n'est pas entièrement correcte de la pile.Exemple:

    private void foo()
    {
        try
        {
            bar(3);
            bar(2);
            bar(1);
            bar(0);
        }
        catch(DivideByZeroException)
        {
            //log message and rethrow...
            throw;
        }
    }

    private void bar(int b)
    {
        int a = 1;
        int c = a/b;  // Generate divide by zero exception.
    }

La trace de la pile des points à l'origine de l'exception correctement (rapporté le numéro de ligne), mais le numéro de la ligne signalée pour les foo() est la ligne de la jeter;déclaration, par conséquent, vous ne peut pas vous dire d'où des appels à bar() a provoqué l'exception.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top