Question

Je regarde l'article C # - transfert de données Objet sur DTO sérialisables.

L'article comprend ce morceau de code:

public static string SerializeDTO(DTO dto) {
    try {
        XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
        StringWriter sWriter = new StringWriter();
        xmlSer.Serialize(sWriter, dto);
        return sWriter.ToString();
    }
    catch(Exception ex) {
        throw ex;
    }
}

Le reste de l'article semble sain d'esprit et raisonnable (à un noob), mais try-catch-jet jette un WtfException ... est-ce pas exactement équivalent à ne pas traiter les exceptions du tout?

Ergo:

public static string SerializeDTO(DTO dto) {
    XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
    StringWriter sWriter = new StringWriter();
    xmlSer.Serialize(sWriter, dto);
    return sWriter.ToString();
}

Ou suis-je manque quelque chose de fondamental sur la gestion des erreurs en C #? Il est à peu près la même chose que Java (moins vérifié exceptions), non? ... Autrement dit, ils ont tous deux raffinés C ++.

La question Stack Overflow La différence entre re-lancer paramètre moins de prise et ne pas faire quoi que ce soit? semble soutenir mon affirmation selon laquelle try-catch-jet est-un no-op.


EDIT:

Pour résumer pour toute personne qui trouve ce fil à l'avenir ...

NE PAS

try {
    // Do stuff that might throw an exception
}
catch (Exception e) {
    throw e; // This destroys the strack trace information!
}

Les informations de trace de la pile peut être cruciale pour identifier la cause racine du problème!

DO

try {
    // Do stuff that might throw an exception
}
catch (SqlException e) {
    // Log it
    if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
        // Do special cleanup, like maybe closing the "dirty" database connection.
        throw; // This preserves the stack trace
    }
}
catch (IOException e) {
    // Log it
    throw;
}
catch (Exception e) {
    // Log it
    throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
    // Normal clean goes here (like closing open files).
}

Attrapez les exceptions plus spécifiques avant les moins spécifiques (comme Java).


Références:

Était-ce utile?

La solution

Tout d'abord; la façon dont le code dans l'article le fait est mal. throw ex réinitialise la pile d'appels à l'exception au point où cette instruction throw est; perdre les informations sur l'endroit où l'exception a été effectivement créé.

En second lieu, si vous venez attraper et re-jeter comme ça, je ne vois aucune valeur ajoutée, l'exemple de code ci-dessus serait tout aussi bon (ou, étant donné le peu de throw ex, encore mieux) sans try-catch.

Cependant, il y a des cas où vous pourriez vouloir attraper et réémettre une exception. L'exploitation forestière pourrait être l'un d'entre eux:

try 
{
    // code that may throw exceptions    
}
catch(Exception ex) 
{
    // add error logging here
    throw;
}

Autres conseils

Ne pas faire,

try 
{
...
}
catch(Exception ex)
{
   throw ex;
}

Vous perdrez les informations de trace de la pile ...

Soit faire,

try { ... }
catch { throw; }

ou

try { ... }
catch (Exception ex)
{
    throw new Exception("My Custom Error Message", ex);
}

L'une des raisons pour lesquelles vous voudrez peut-être réémettre est si vous manipulez différentes exceptions, par exemple.

try
{
   ...
}
catch(SQLException sex)
{
   //Do Custom Logging 
   //Don't throw exception - swallow it here
}
catch(OtherException oex)
{
   //Do something else
   throw new WrappedException("Other Exception occured");
}
catch
{
   System.Diagnostics.Debug.WriteLine("Eeep! an error, not to worry, will be handled higher up the call stack");
   throw; //Chuck everything else back up the stack
}

C # (avant C # 6) ne supporte pas CIL « exceptions filtrées », qui VB fait, et donc en C # 1-5 l'une des raisons pour re-lancer une exception est que vous n'avez pas assez d'informations au moment de catch () pour déterminer si vous vouliez attraper en fait l'exception.

Par exemple, en VB que vous pouvez faire

Try
 ..
Catch Ex As MyException When Ex.ErrorCode = 123
 .. 
End Try

... qui ne traiterait pas MyExceptions avec des valeurs différentes ERRORCODE. En C # avant v6, vous devez attraper et re-jeter le MyException si le ErrorCode était pas 123:

try 
{
   ...
}
catch(MyException ex)
{
    if (ex.ErrorCode != 123) throw;
    ...
}

Depuis C # 6.0, vous pouvez filtrer comme avec VB:

try 
{
  // Do stuff
} 
catch (Exception e) when (e.ErrorCode == 123456) // filter
{
  // Handle, other exceptions will be left alone and bubble up
}

La raison principale pour avoir le code comme:

try
{
    //Some code
}
catch (Exception e)
{
    throw;
}

est si je peux avoir un point d'arrêt dans la prise, qui a un objet d'exception instancié. Je fais beaucoup tout en développant / débogage. Bien sûr, le compilateur me donne un avertissement sur tous les années inutilisés de e, et idéalement, ils doivent être enlevés avant une version release.

Ils sont bien au cours du débogage bien.

Une raison valable pour rethrowing exceptions peut être que vous souhaitez ajouter des informations à l'exception, ou peut-être envelopper l'exception d'origine dans un de vos propres décisions:

public static string SerializeDTO(DTO dto) {
  try {
      XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
      StringWriter sWriter = new StringWriter();
      xmlSer.Serialize(sWriter, dto);
      return sWriter.ToString();
  }
  catch(Exception ex) {
    string message = 
      String.Format("Something went wrong serializing DTO {0}", DTO);
    throw new MyLibraryException(message, ex);
  }
}
  

est-ce pas exactement équivalent à ne pas   la gestion des exceptions du tout?

Mais non, ce n'est pas la même chose. Il remet à zéro le stacktrace de l'exception. Bien que je suis d'accord que c'est probablement une erreur, et donc un exemple de code mauvais.

Vous ne voulez pas jeter ex - car cela perdra la pile d'appel. Voir Exception Handling (MSDN).

Et oui, le try ... catch ne fait rien utile (en dehors de perdre la pile d'appel - il est en fait pire - à moins que pour une raison quelconque vous ne voulez pas exposer ces informations).

Un point que les gens n'ont pas mentionné est que si les langages .NET ne font pas vraiment une distinction appropriée, la question de savoir si l'on doit prendre des mesures lorsqu'une exception se produit, et que l'on va résolution il, des questions sont effectivement distinctes. Il y a beaucoup de cas où l'on doit prendre des mesures sur la base d'exceptions on n'a pas l'espoir de résoudre, et il y a des cas où tout ce qui est nécessaire pour « résoudre » une exception pour se détendre la pile jusqu'à un certain point - aucune autre mesure nécessaire .

En raison de la sagesse commune que l'on ne devrait porter que les choses « attraper » on peut « gérer », beaucoup de code qui devrait intervenir lorsque des exceptions se produisent, ne fonctionne pas. Par exemple, beaucoup de code un verrou, placez l'objet gardé « temporairement » dans un état qui viole ses invariants, puis mettre un objet en un état légitime, puis relâchez le verrou arrière avant que quelqu'un d'autre ne peut voir l'objet. Si une exception se produit alors que l'objet est dans un état dangereusement invalide, la pratique courante consiste à libérer le verrou avec l'objet toujours dans cet état. Un bien meilleur modèle serait d'avoir une exception qui se produit alors que l'objet est dans un état « dangereux » invalide expressément le verrou de sorte que toute tentative future de l'acquérir échouera immédiatement. L'utilisation régulière d'un tel modèle permettrait d'améliorer grandement la sécurité de ce qu'on appelle la gestion des exceptions « Pokemon », ce qui à mon humble avis obtient une mauvaise réputation en raison principalement de code qui permet des exceptions percoler sans prendre des mesures appropriées en premier.

Dans la plupart des langages .NET, la seule façon pour le code de prendre des mesures sur la base d'une exception est à catch (même si elle sait qu'elle ne va pas résoudre l'exception), effectuer l'action en question, puis re-throw) . Une autre approche possible si le code ne se soucie pas de ce que exception est levée est d'utiliser un drapeau ok avec un bloc de try/finally; mettre le drapeau ok à false avant le bloc, et true avant que les sorties de bloc, et avant toute return qui est dans le bloc. Puis, dans finally, supposons que si ok n'est pas définie, une exception doit avoir eu lieu. Une telle approche est sémantiquement mieux qu'un catch / throw, mais il est laid et est moins maintenable que ce qu'elle devrait être.

Une raison possible d'attraper un jet-est de désactiver tous les filtres d'exception plus profonde la pile de filtrage vers le bas ( ancien lien aléatoire ). Mais bien sûr, si cela était l'intention, il y aurait un commentaire il le dire.

Cela dépend de ce que vous faites dans le bloc catch, et si vous êtes désireux de passer l'erreur sur le code d'appel ou non.

Vous pourriez dire Catch io.FileNotFoundExeption ex et ensuite utiliser un chemin de fichier alternatif ou un tel, mais encore jeter l'erreur sur.

Aussi faire Throw au lieu de Throw Ex vous permet de garder la trace complète de la pile. Throw ex redémarre la trace de la pile de l'instruction throw (j'espère que le bon sens).

Alors que la plupart des autres réponses fournissent de bons exemples des raisons pour lesquelles vous voudrez peut-être prendre un rethrow une exception, personne ne semble avoir mentionné un scénario « enfin ».

Un exemple de c'est là que vous avez une méthode dans laquelle vous définissez le curseur (par exemple à un curseur d'attente), la méthode a plusieurs points de sortie (par exemple si () return;) et vous voulez vous assurer que le curseur est remis à zéro à la fin de la méthode.

Pour ce faire, vous pouvez envelopper tout le code dans un try / catch / finally. Dans le enfin placer le curseur vers la droite curseur. Alors que vous n'enterrez aucune exception valable, réémettre dans les prises.

try
{
    Cursor.Current = Cursors.WaitCursor;
    // Test something
    if (testResult) return;
    // Do something else
}
catch
{
    throw;
}
finally
{
     Cursor.Current = Cursors.Default;
}

Cela peut être utile lorsque vos fonctions de programmation pour une bibliothèque ou dll.

Cette structure rethrow peut être utilisé pour réinitialiser la pile délibérément appel de sorte qu'au lieu de voir l'exception jetée d'une fonction individuelle à l'intérieur de la fonction, vous obtenez l'exception de la fonction elle-même.

Je pense que cela est juste utilisé pour que les exceptions lancées sont plus propres et ne vont pas dans les « racines » de la bibliothèque.

Dans l'exemple dans le code que vous avez posté il y a, en fait, ne sert à rien attraper l'exception car il n'y a rien fait sur la prise, il est tout simplement ré-thown, en fait, il fait plus de mal que de bien que l'appel pile est perdu.

Vous, attraper toutefois une exception à faire une certaine logique (par exemple la fermeture de connexion sql de verrouillage de fichiers, ou tout simplement une exploitation forestière) en cas d'exception le lancer de nouveau au code appelant à traiter. Ce serait plus fréquent dans une couche d'affaires que le code final avant que vous pouvez le codeur mettre en œuvre votre couche métier pour gérer l'exception.

Pour re-itérer si le Il ne sert à attraper l'exception dans l'exemple que vous avez publié. NE PAS le faire comme ça!

Désolé, mais beaucoup d'exemples comme « une conception améliorée » odeur encore horriblement ou peut être extrêmement trompeur. Ayant prises try {} {log; jet} est juste totalement inutile. l'exploitation forestière d'exception devrait être fait à la place centrale dans l'application. exceptions bulle la stacktrace de toute façon, pourquoi ne pas les connecter quelque part et à proximité des frontières du système?

Il faut être prudent lorsque vous sérialiser votre contexte (par exemple DTO dans un exemple donné) juste dans le message du journal. Il peut facilement contenir des informations sensibles on peut vouloir ne pas atteindre les mains de toutes les personnes qui peuvent accéder aux fichiers journaux. Et si vous n'ajoutez aucune information nouvelle à l'exception, je ne vois vraiment pas le point d'emballage d'exception. Le bon vieux Java a un moment donné pour cela, il faut l'appelant de savoir quel genre d'exceptions on devrait attendre d'appeler alors le code. Puisque vous n'avez pas dans .NET, l'emballage ne le fait pas bien sur au moins 80% des cas, je l'ai vu.

En plus de ce que les autres ont dit, voir ma réponse à une question connexe qui montre que la capture et rethrowing n'est pas un non-op (il est en VB, mais une partie du code pourrait être C # appelé à partir de VB).

La plupart des réponses parler de scénario catch-log-rethrow.

Au lieu d'écrire dans votre code envisager d'utiliser AOP, en particulier

scroll top