instruction switch - gestion du cas par défaut lorsqu'il ne peut pas être atteint


14

Si j'utilise une instruction switch pour gérer les valeurs d'une énumération (qui appartient à ma classe) et que j'ai une casse pour chaque valeur possible - cela vaut-il la peine d'ajouter du code pour gérer le cas «par défaut»?

enum MyEnum
{
    MyFoo,
    MyBar,
    MyBat
}

MyEnum myEnum = GetMyEnum();
switch (myEnum)
{
    case MyFoo:
        DoFoo();
        break;
    case MyBar:
        DoBar();
        break;
    case MyBat:
        DoBat();
        break;
    default:
        Log("Unexpected value");
        throw new ArgumentException() 
}

Je ne pense pas que ce soit parce que ce code ne peut jamais être atteint (même avec des tests unitaires). Mon collègue n'est pas d'accord et pense que cela nous protège contre les comportements inattendus causés par l'ajout de nouvelles valeurs à MyEnum.

Que dites-vous, communauté?


Disons que MyEnum est un type non nullable.
sd

3
"Aujourd'hui", il est non nul. Et demain quand vous ne maintenez plus le code. Ou qu'en est-il lorsque "MyBiz" est ajouté à l'énumération, mais pas le cas? Les commentaires de Caleb sur la maintenance sont très pertinents.

1
Apprenez à votre compilateur qu'il s'agit d'une erreur fatale s'il existe un commutateur ne couvrant pas tous les cas.

Que se passe-t-il si quelqu'un jette une valeur non valide pour MyEnumensuite la transmettre via votre commutateur?
Mawg dit réintégrer Monica le

1
Quelle langue? Si Java, vous devez mettre une méthode à l'intérieur de l'énumération et simplement l'appeler (polymorphisme), en éliminant switchcomplètement l' instruction.
user949300

Réponses:


34

Inclure le cas par défaut ne change pas la façon dont votre code fonctionne, mais il rend votre code plus maintenable. En rendant le code cassé de manière évidente (enregistrez un message et lancez une exception), vous incluez une grosse flèche rouge pour le stagiaire que votre entreprise embauche l'été prochain pour ajouter quelques fonctionnalités. La flèche indique: "Hé, vous! Oui, je vous parle! Si vous voulez ajouter une autre valeur à l'énumération, vous feriez mieux d'ajouter un cas ici aussi." Cet effort supplémentaire pourrait ajouter quelques octets au programme compilé, ce qui est quelque chose à considérer. Mais cela sauvera aussi quelqu'un (peut-être même le futur, vous) entre une heure et une journée de gratte-tête improductif.


Mise à jour: La situation décrite ci-dessus, c'est-à-dire la protection contre les valeurs ajoutées à une énumération ultérieurement, peut également être détectée par le compilateur. Clang (et gcc, je pense) émet par défaut un avertissement si vous activez un type énuméré mais que vous n'avez pas de cas qui couvre toutes les valeurs possibles dans l'énumération. Ainsi, par exemple, si vous supprimez le defaultcas de votre commutateur et ajoutez une nouvelle valeur MyBazà l'énumération, vous obtiendrez un avertissement qui dit:

Enumeration value 'MyBaz' not handled in switch

Laisser le compilateur détecter les cas non couverts est qu'il élimine largement le besoin de ce defaultcas inaccessible qui a inspiré votre question en premier lieu.


2
Ok, vous m'avez convaincu :) Je vais juste devoir accepter la brèche dans mes numéros de couverture de code.
sd

@st Il n'y a aucune raison que vous ne puissiez pas tester ce code. Faites simplement un build de test qui compile conditionnellement une valeur supplémentaire dans votre énumération, puis écrivez un test unitaire qui l'utilise. Ce n'est peut-être pas idéal, mais ce n'est probablement pas le seul cas où vous devez tester du code qui ne sera normalement jamais atteint.
Caleb

Ou, vous convertissez une valeur non énumérée en votre type d'énumération et l'utilisez.
Mawg dit de réintégrer Monica le

5

J'en parlais également avec un collègue ce matin - c'est vraiment dommage, mais je pense que la gestion du défaut est nécessaire pour la sécurité, pour deux raisons:

Tout d'abord, comme le mentionne votre collègue, il protège le code du futur contre les nouvelles valeurs ajoutées à l'énumération. Cela peut ou non sembler être une possibilité, mais elle est toujours là.

Plus important encore, selon le langage / compilateur, il peut être possible d'avoir des valeurs qui ne sont pas membres de l'énumération dans votre variable commutée. Par exemple, en C #:

MyEnum myEnum = (MyEnum) 3; // This could come from anywhere, maybe parsed from text?
// ... code goes on for a while

switch ( myEnum )
{
    case MyEnum.A:
        // ... handle A case
        break;
    case MyEnum.B:
        // ... handle B case
        break;
}

// ... code that expects either A or B to have happened

En ajoutant le simple case default:et en levant une exception, vous vous êtes protégé contre ce cas étrange où "rien" ne se produit mais "quelque chose" aurait dû se produire.

Malheureusement, à chaque fois que j'écris une instruction switch, c'est parce que je vérifie les cas d'une énumération. Je souhaite vraiment que le comportement "lancer par défaut" puisse être appliqué par la langue elle-même (au moins en ajoutant un mot-clé).


3

L'ajout d'un cas par défaut même si vous ne vous attendez jamais à l'atteindre peut être une bonne chose. Cela facilitera le débogage si votre code lève immédiatement une exception «Cela n'aurait pas dû arriver» plutôt que plus tard dans le programme, lançant une exception mystérieuse ou renvoyant des résultats inattendus sans erreur.


2

Je dis:

Essayez d'ajouter un autre type à MyEnum. Modifiez ensuite cette ligne:

MyEnum myEnum = GetMyEnum();

à

MyEnum myEnum = SomethingElse;

Exécutez ensuite votre code avec le cas par défaut et sans le cas par défaut. Quel comportement préférez-vous?

Avoir le cas par défaut peut également être utile pour intercepter des NULLvaleurs et les empêcher NullPointerExceptions.


-1

Si vous aviez une idée de la façon de gagner du temps en insistant sur le cas par défaut, vous n'auriez pas besoin de poser la question. Ne rien faire en silence dans une condition d'erreur n'est pas acceptable - détectez-vous en silence des exceptions qui ne devraient jamais se produire? Laisser des "mines" pour les programmeurs qui vous suivent, tout aussi inacceptable.

Si votre code ne sera jamais changé ou modifié, et qu'il est 100% sans bogue, laisser de côté le cas par défaut peut être OK.

Ada (le grand-père des langages de programmation robustes) ne compilera pas de commutateur sur une énumération, à moins que toutes les énumérations soient couvertes ou qu'il y ait un gestionnaire par défaut - cette fonctionnalité est sur ma liste de souhaits pour chaque langue. Notre norme de codage Ada stipule qu'avec les commutateurs sur les énumérations, la gestion explicite de toutes les valeurs, sans valeur par défaut, est la manière préférée de gérer cette situation.


Pourquoi tous les -1?
mattnz

2
Je ne vous ai pas -1, mais je pense que ce serait à cause de votre attitude;)
Friek
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.