Question Attraper plusieurs exceptions à la fois?


Il est déconseillé de simplement attraper System.Exception. Au lieu de cela, seules les exceptions "connues" doivent être interceptées.

Maintenant, cela conduit parfois à un code répétitif inutile, par exemple:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

Je me demande: Y at-il un moyen d'attraper les deux exceptions et appelez seulement le WebId = Guid.Empty appeler une fois?

L'exemple donné est plutôt simple, car ce n'est qu'un GUID. Mais imaginez du code où vous modifiez un objet plusieurs fois, et si l'une des manipulations échoue de manière attendue, vous voulez "réinitialiser" le object. Cependant, s'il y a une exception inattendue, je veux quand même l'augmenter.


1709
2017-09-25 20:56


origine


Réponses:


Capture System.Exception et allumer les types

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

1788
2017-09-25 21:01



MODIFIER: Je suis d'accord avec les autres qui disent que, à partir de C # 6.0, les filtres d'exception sont maintenant une excellente façon de procéder: catch (Exception ex) when (ex is ... || ex is ... )

Sauf que je déteste encore un peu la mise en page d'une ligne longue et je poserais personnellement le code comme suit. Je pense que c'est aussi fonctionnel qu'esthétique, car je crois que cela améliore la compréhension. Certains peuvent être en désaccord:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

ORIGINAL:

Je sais que je suis un peu en retard à la fête ici, mais de la fumée sacrée ...

Couper directement à la chasse, ce genre de dupliquer une réponse plus tôt, mais si vous voulez vraiment effectuer une action commune pour plusieurs types d'exception et garder le tout net et bien rangé dans le cadre de la méthode unique, pourquoi ne pas utiliser un lambda / fermeture / fonction inline pour faire quelque chose comme ce qui suit? Je veux dire, les chances sont assez bonnes que vous finirez par réaliser que vous voulez juste faire de cette fermeture une méthode séparée que vous pouvez utiliser partout. Mais alors ce sera super facile de le faire sans réellement changer le reste du code structurellement. Droite?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

Je ne peux pas m'empêcher de me demander (Attention: un peu d'ironie / sarcasme à venir) pourquoi diable aller à tous ces efforts pour remplacer simplement ce qui suit:

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

... avec une variation folle de cette prochaine odeur de code, je veux dire par exemple, seulement pour prétendre que vous enregistrez quelques frappes.

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

Parce que ce n'est certainement pas plus lisible automatiquement.

Certes, j'ai quitté les trois instances identiques de /* write to a log, whatever... */ return; sur le premier exemple.

Mais c'est en quelque sorte mon point de vue. Vous avez entendu parler des fonctions / méthodes, n'est-ce pas? Sérieusement. Ecrire un commun ErrorHandler fonction et, comme, l'appeler de chaque bloc catch.

Si vous me demandez, le deuxième exemple (avec le if et is mots-clés) est à la fois significativement moins lisible et simultanément beaucoup plus sujet aux erreurs pendant la phase de maintenance de votre projet.

La phase de maintenance, pour quiconque est relativement nouveau dans la programmation, représentera 98,7% ou plus de la durée de vie globale de votre projet, et le pauvre schmuck qui s'occupe de la maintenance sera presque certainement quelqu'un d'autre que vous. Et il y a une très bonne chance qu'ils passent 50% de leur temps au travail en maudissant votre nom.

Et bien sûr, FxCop aboie à vous et vous devez donc aussiajouter un attribut à votre code qui a exactement le zip à faire avec le programme en cours, et est seulement là pour dire à FxCop d'ignorer un problème qui dans 99,9% des cas, il est totalement correct dans le marquage. Et, désolé, je peux me tromper, mais cet attribut "ignorer" n'est-il pas réellement compilé dans votre application?

Mettrait l'ensemble if test sur une ligne le rend plus lisible? Je ne pense pas. Je veux dire, j'ai eu un autre programmeur véhémentement argumenter il y a longtemps que mettre plus de code sur une ligne le ferait «courir plus vite». Mais bien sûr, il était complètement fou. En essayant de lui expliquer (avec un visage impassible - ce qui était difficile) comment l'interprète ou le compilateur diviserait cette longue ligne en phrases discrètes d'une instruction par ligne - essentiellement identique au résultat s'il était parti devant et juste fait le code lisible au lieu d'essayer de surpasser le compilateur - n'a eu aucun effet sur lui que ce soit. Mais je m'égare.

Combien Moins lisible obtient-il lorsque vous ajoutez trois types d'exception, un mois ou deux à partir de maintenant? (Réponse: il obtient un lot moins lisible).

L'un des points importants, en fait, c'est que la plupart du formatage du code source textuel que nous regardons tous les jours est de rendre vraiment, vraiment évident pour les autres êtres humains ce qui se passe réellement lorsque le code s'exécute. Parce que le compilateur transforme le code source en quelque chose de totalement différent et ne se soucie pas du style de mise en forme de votre code. Donc tout sur une seule ligne est vraiment nul.

Je dis juste ...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

370
2017-10-12 00:24



Comme d'autres l'ont souligné, vous pouvez avoir un if déclaration à l'intérieur de votre bloc catch pour déterminer ce qui se passe. C # 6 supporte les filtres d'exception, donc ce qui suit fonctionnera:

try { … }
catch (Exception e) when (MyFilter(e))
{
    …
}

le MyFilter méthode pourrait alors ressembler à ceci:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

Alternativement, tout peut être fait en ligne (le côté droit de l'instruction when doit juste être une expression booléenne).

try { … }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    …
}

Ceci est différent de l'utilisation d'un if déclaration de l'intérieur du catch bloquer, en utilisant des filtres d'exception ne sera pas dérouler la pile.

Vous pouvez télécharger Visual Studio 2015 pour vérifier cela.

Si vous souhaitez continuer à utiliser Visual Studio 2013, vous pouvez installer le package nuget suivant:

Install-Package Microsoft.Net.Compilers

Au moment de la rédaction, cela inclura le support pour C # 6.

En référençant ce paquet, le projet sera construit en utilisant le   version spécifique des compilateurs C # et Visual Basic contenus dans   package, par opposition à toute version installée par le système.


239
2018-04-04 13:59



Pas en C # malheureusement, car vous auriez besoin d'un filtre d'exception pour le faire et C # n'expose pas cette fonctionnalité de MSIL. VB.NET a cependant cette capacité, par ex.

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

Ce que vous pouvez faire est d'utiliser une fonction anonyme pour encapsuler votre code sur erreur, puis l'appeler dans ces blocs catch spécifiques:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

184
2017-09-25 21:03



Par souci d'exhaustivité, puisque .NET 4.0le code peut être réécrit comme:

Guid.TryParse(queryString["web"], out WebId);

TryParse ne lance jamais d'exceptions et renvoie false si le format est incorrect, en définissant WebId sur Guid.Empty.


Depuis C # 7 vous pouvez éviter d'introduire une variable sur une ligne distincte:

Guid.TryParse(queryString["web"], out Guid webId);

Vous pouvez également créer des méthodes pour analyser les tuples retournés, qui ne sont pas encore disponibles dans .NET Framework à partir de la version 4.6:

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

Et utilisez-les comme ceci:

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

La prochaine mise à jour inutile de cette réponse inutile vient quand la déconstruction des out-paramètres est implémentée en C # 12. :)


122
2018-04-13 12:18



Si vous pouvez mettre à jour votre application à C # 6, vous avez de la chance. La nouvelle version C # a implémenté des filtres Exception. Donc vous pouvez écrire ceci:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

Certaines personnes pensent que ce code est le même que

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

Mais ce n'est pas. En fait c'est la seule nouvelle fonctionnalité de C # 6 qu'il n'est pas possible d'émuler dans les versions précédentes. Tout d'abord, un nouveau lancer signifie plus de frais généraux que de sauter la capture. Deuxièmement, ce n'est pas sémantiquement équivalent. La nouvelle fonctionnalité préserve la pile intacte lorsque vous déboguez votre code. Sans cette fonctionnalité, le vidage sur incident est moins utile ou même inutile.

Voir un discussion à ce sujet sur CodePlex. Et un exemple montrant la différence.


62
2018-04-01 12:29



Si vous ne voulez pas utiliser un if déclaration dans le catch étendues, dans C# 6.0 vous pouvez utiliser Exception Filters syntaxe qui était déjà supporté par le CLR dans les versions previews mais qui n'existait que dans VB.NET/MSIL:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

Ce code va attraper le Exception seulement quand c'est un InvalidDataException ou ArgumentNullException.

En fait, vous pouvez mettre fondamentalement n'importe quelle condition à l'intérieur de ce when clause:

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

Notez que par opposition à un if déclaration à l'intérieur du catchla portée, Exception Filters ne peut pas jeter Exceptions, et quand ils le font, ou quand la condition n'est pas true, le suivant catch la condition sera évaluée à la place:

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Sortie: prise générale.

Quand il y en a plus d'un true  Exception Filter - le premier sera accepté:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

Sortie: Catch.

Et comme vous pouvez le voir dans le MSIL le code n'est pas traduit en if déclarations, mais à Filters, et Exceptions ne peut pas être jeter à l'intérieur des zones marquées avec Filter 1 et Filter 2 mais le filtre jetant le Exception échouera à la place, aussi la dernière valeur de comparaison poussée à la pile avant la endfilter commande déterminera le succès / l'échec du filtre (Catch 1  XOR  Catch 2 exécutera en conséquence):

Exception Filters MSIL

En outre, spécifiquement Guid a la Guid.TryParse méthode.


26
2017-10-07 17:31



La réponse acceptée semble acceptable, sauf que CodeAnalysis /FxCop se plaindre du fait qu'il attrape un type d'exception générale.

En outre, il semble que l'opérateur "est" peut dégrader légèrement les performances.

CA1800: Ne pas lancer inutilement dit "envisager de tester le résultat de l'opérateur" as "à la place", mais si vous faites cela, vous allez écrire plus de code que si vous attrapez chaque exception séparément.

De toute façon, voici ce que je ferais:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

19
2017-07-30 17:09



C'est une variante de la réponse de Matt (j'ai l'impression que c'est un peu plus propre) ... utilisez une méthode:

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

Toutes les autres exceptions seront levées et le code WebId = Guid.Empty; ne sera pas touché. Si vous ne voulez pas que d'autres exceptions viennent planter votre programme, il suffit d'ajouter ceci APRÈS les deux autres captures:

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

18
2017-08-31 20:51