Quelle est la bonne façon de relancer une exception dans C#? [dupliquer]

cette question a déjà une réponse ici:

j'ai une question pour vous qui vient de mon partenaire faisant les choses d'une manière différente que je fais.

est-il préférable de le faire :

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

ou ceci:

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

Font-ils la même chose? Est-ce mieux que les autres?

386
demandé sur Tim Post 2008-10-07 17:33:55

9 réponses

vous devriez toujours utiliser la syntaxe suivante pour réécrire une exception, sinon vous piétinerez la trace de la pile:

throw;

si vous imprimez la trace résultant de "throw ex", vous verrez qu'il se termine sur cette déclaration et non à la source réelle de l'exception.

en gros, il devrait être considéré comme une infraction pénale d'utiliser "jeter ex".

695
répondu Torbjörn Gyllebring 2014-09-17 23:44:31

mes préférences est d'utiliser

try 
{
}
catch (Exception ex)
{
     ...
     throw new Exception ("Put more context here", ex)
}

cela préserve l'erreur originale, mais vous permet de mettre plus de contexte, comme un ID d'objet, une chaîne de connexion, des trucs comme ça. Souvent, mon outil de rapport d'exception aura 5 enchaînés d'exceptions à déclarer, chaque rapport plus de détails.

144
répondu RB. 2008-10-07 13:43:24

si vous lancez une exception avec out une variable (le deuxième exemple) la chaîne de Stac comprendra la méthode originale qui a jeté l'exception.

dans le premier exemple, le StackTrace sera modifié pour refléter la méthode actuelle.

exemple:

static string ReadAFile(string fileName) {
    string result = string.Empty;
    try {
        result = File.ReadAllLines(fileName);
    } catch(Exception ex) {
        throw ex; // This will show ReadAFile in the StackTrace
        throw;    // This will show ReadAllLines in the StackTrace
    }
31
répondu funkwurm 2017-11-06 11:08:14

le premier conserve la trace originale de la pile de l'exception, le second la remplace par l'emplacement actuel.

donc la première est de loin la meilleure.

22
répondu Quibblesome 2008-10-07 13:37:16

je sais que c'est une vieille question, mais je vais répondre parce que j'ai pas d'accord avec toutes les réponses ici.

maintenant, je suis d'accord que la plupart du temps vous voulez soit faire un throw simple , pour préserver autant d'informations que possible sur ce qui a mal tourné, ou vous voulez jeter une nouvelle exception qui peut contenir que comme une exception intérieure, ou non, selon la mesure dans laquelle vous êtes susceptible de vouloir savoir sur les événements intérieurs qui l'ont causé.

il y a une exception cependant. Il y a plusieurs cas où une méthode fera appel à une autre méthode et une condition qui provoque une exception dans l'appel intérieur devrait être considérée la même exception sur l'appel extérieur.

un exemple est une collection spécialisée implémentée en utilisant une autre collection. Disons que c'est un DistinctList<T> qui enveloppe un List<T> mais refuse les articles en double.

si quelqu'un a appelé ICollection<T>.CopyTo sur votre classe collection, il pourrait être juste un appel direct à CopyTo sur la collection interne (si dire, toute la logique personnalisée appliquée uniquement à l'ajout à la collection, ou la mise en place). Maintenant, les conditions dans lesquelles cet appel serait lancé sont exactement les mêmes que celles dans lesquelles votre collection devrait lancer pour correspondre à la documentation de ICollection<T>.CopyTo .

Maintenant, vous ne pouviez pas attraper l'exécution du tout,et le laisser passer. Ici, bien que l'utilisateur obtienne une exception de List<T> quand ils appelaient quelque chose sur un DistinctList<T> . Pas la fin du monde, mais vous pouvez masquer les détails d'implémentation.

ou vous pouvez faire votre propre vérification:

public CopyTo(T[] array, int arrayIndex)
{
  if(array == null)
    throw new ArgumentNullException("array");
  if(arrayIndex < 0)
    throw new ArgumentOutOfRangeException("arrayIndex", "Array Index must be zero or greater.");
  if(Count > array.Length + arrayIndex)
    throw new ArgumentException("Not enough room in array to copy elements starting at index given.");
  _innerList.CopyTo(array, arrayIndex);
}

ce n'est pas le pire code parce que c'est boilerplate et nous pouvons probablement le copier à partir d'une autre implémentation de CopyTo où ce n'était pas un simple passage et nous avons dû l'implémenter nous-mêmes. Pourtant, c'est de répéter inutilement les exactement les mêmes vérifications qui vont être faites dans _innerList.CopyTo(array, arrayIndex) , donc la seule chose qu'il a ajouté à notre code est 6 lignes où il pourrait y avoir un bug.

Nous avons pu vérifier et enveloppez:

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentNullException ane)
  {
    throw new ArgumentNullException("array", ane);
  }
  catch(ArgumentOutOfRangeException aore)
  {
    throw new ArgumentOutOfRangeException("Array Index must be zero or greater.", aore);
  }
  catch(ArgumentException ae)
  {
    throw new ArgumentException("Not enough room in array to copy elements starting at index given.", ae);
  }
}

en termes de nouveau code ajouté qui pourrait potentiellement être buggy, c'est encore pire. Et nous n'avons rien à gagner des exceptions intérieures. Si nous passons un tableau null à cette méthode et recevons un ArgumentNullException , nous n'allons pas à apprenez n'importe quoi en examinant l'exception intérieure et en apprenant qu'un appel à _innerList.CopyTo a été passé un tableau nul et a lancé un ArgumentNullException .

Ici, nous pouvons faire tout ce que nous voulons avec:

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentException ae)
  {
    throw ae;
  }
}

toutes les exceptions que nous espérons devoir jeter si l'utilisateur l'appelle avec des arguments incorrects, seront correctement lancées par ce nouveau jet. S'il y a un bug dans la logique utilisée ici, c'est dans l'une des deux lignes - soit nous avions tort en décidant cela, nous avons choisi un cas où cette approche fonctionne, ou nous avons eu tort d'avoir ArgumentException comme type d'exception recherché. C'est les deux seuls insectes que le catch block peut avoir.

maintenant. Je suis toujours d'accord que la plupart du temps vous voulez soit un throw; simple ou vous voulez construire votre propre exception pour correspondre plus directement le problème de la perspective de la méthode en question. Il y a des cas comme celui-ci où re-lancer comme ceci fait plus de sens, et il y a beaucoup d'autres cas. Par exemple: pour prendre un exemple très différent, si un lecteur de fichiers ATOM implémenté avec un FileStream et un XmlTextReader reçoit une erreur de fichier ou un XML invalide, alors il voudra peut-être jeter exactement la même exception qu'il a reçu de ces classes, mais il devrait regarder à l'appelant que c'est AtomFileReader qui lance un FileNotFoundException ou XmlException , de sorte qu'ils pourraient être candidats pour un lancement similaire.

Modifier:

on peut aussi combiner les deux:

public CopyTo(T[] array, int arrayIndex)
{
  try
  {
    _innerList.CopyTo(array, arrayIndex);
  }
  catch(ArgumentException ae)
  {
    throw ae;
  }
  catch(Exception ex)
  {
    //we weren't expecting this, there must be a bug in our code that put
    //us into an invalid state, and subsequently let this exception happen.
    LogException(ex);
    throw;
  }
}
16
répondu Jon Hanna 2012-08-13 11:54:26

vous devriez toujours utiliser "jeter;" pour rethrow les exceptions dans .NET,

Consultez ce, http://weblogs.asp.net/bhouse/archive/2004/11/30/272297.aspx

Fondamentalement MSIL (CIL) a deux instructions - "jeter" et "renvoyer" et "C#" s "throw ex;" sera compilé en MSIL "jeter" et "C#" s "jeter;" - en MSIL "renvoyer"! Fondamentalement, je peux voir la raison pour laquelle "jeter ex" l'emporte sur la trace de la pile.

8
répondu Vinod T. Patil 2010-07-05 13:29:24

le premier est mieux. Si vous essayez de déboguer la seconde et de regarder la pile d'appels, vous ne verrez pas d'où vient l'exception originale. Il y a des trucs pour garder la pile d'appels intacte (essayez la recherche, elle a déjà été répondue) si vous avez vraiment besoin de rethrow.

4
répondu Mendelt 2008-10-07 13:37:34

j'ai trouvé que si l'exception est jetée dans la même méthode qu'elle est attrapée, la trace de pile n'est pas préservée, pour ce que cela vaut.

void testExceptionHandling()
{
    try
    {
        throw new ArithmeticException("illegal expression");
    }
    catch (Exception ex)
    {
        throw;
    }
    finally
    {
        System.Diagnostics.Debug.WriteLine("finally called.");
    }
}
3
répondu James 2011-01-08 00:00:45

ça dépend. Dans une construction de débogage, je veux voir la trace originale de la pile avec le moins d'effort possible. Dans ce cas, "jeter;" correspond à la facture. Dans une construction de version, cependant, (a) je veux enregistrer l'erreur avec la trace de pile originale incluse, et une fois que c'est fait, (b) refashion la manipulation d'erreur pour faire plus de sens à l'utilisateur. Ici "jeter L'Exception" a du sens. Il est vrai que le fait de repenser l'erreur écarte la trace de la pile d'origine, mais un non-développeur n'obtient rien en voyant empilez l'information de trace donc il est correct de rethrow l'erreur.

        void TrySuspectMethod()
        {
            try
            {
                SuspectMethod();
            }
#if DEBUG
            catch
            {
                //Don't log error, let developer see 
                //original stack trace easily
                throw;
#else
            catch (Exception ex)
            {
                //Log error for developers and then 
                //throw a error with a user-oriented message
                throw new Exception(String.Format
                    ("Dear user, sorry but: {0}", ex.Message));
#endif
            }
        }

la façon dont la question est formulée," jeter: "vs." jeter ex; " en fait un peu une diversion. Le vrai choix est entre " jeter;" et "jeter Exception," où "jeter ex;" est un cas spécial peu probable de " jeter Exception."

3
répondu Perry Tribolet 2011-07-15 14:46:31