web-dev-qa-db-fra.com

Existe-t-il une différence significative entre utiliser if / else et switch-case en C #?

Quel est l’avantage/inconvénient d’utiliser une instruction switch par rapport à un if/else en C #. Je ne peux pas imaginer qu'il y ait une telle différence, à part peut-être l'apparence de votre code.

Y a-t-il une raison pour laquelle l'IL résultant ou les performances d'exécution associées seraient radicalement différents?

Connexes: Qu'est-ce qui est plus rapide, allume chaîne ou elseif sur type?

203
Matthew M. Osborn

L'instruction SWITCH produit uniquement le même assemblage que les IF en mode débogage ou compatibilité. Lors de la publication, il sera compilé dans une table de saut (via l'instruction 'switch' de MSIL) - qui est O (1).

C # (contrairement à beaucoup d'autres langages) permet également d'activer les constantes de chaîne - et cela fonctionne un peu différemment. Il n’est évidemment pas pratique de construire des tables de sauts pour des chaînes de longueurs arbitraires. Par conséquent, le plus souvent, un tel commutateur sera compilé dans une pile de FI.

Mais si le nombre de conditions est suffisant pour couvrir les frais généraux, le compilateur C # crée un objet HashTable, le remplit avec des constantes de chaîne et effectue une recherche sur cette table, suivie d'un saut. La table de hachage n'est pas strictement O(1) et a des coûts constants notables, mais si le nombre d'étiquettes de casse est important, il sera nettement plus rapide que de comparer à chaque constante de chaîne dans les FI.

En résumé, si le nombre de conditions est supérieur à 5 ou plus, préférez SWITCH à IF, sinon utilisez ce qui vous convient le mieux.

314
ima

En général (en tenant compte de tous les langages et de tous les compilateurs), une instruction switch PEUT PARFOIS être plus efficace qu'une instruction if/else, car il est facile pour un compilateur de générer des tables de saut à partir d'instructions switch. Il est possible de faire la même chose pour les déclarations if/else, compte tenu des contraintes appropriées, mais cela est beaucoup plus difficile.

Dans le cas de C #, c'est également vrai, mais pour d'autres raisons.

Avec un grand nombre de chaînes, l'utilisation d'une instruction switch présente un avantage significatif en termes de performances, car le compilateur utilise une table de hachage pour implémenter le saut.

Avec un petit nombre de chaînes, la performance entre les deux est la même.

En effet, dans ce cas, le compilateur C # ne génère pas de table de saut. Au lieu de cela, il génère un MSIL équivalent à des blocs IF/ELSE.

Il existe une instruction MSIL "instruction de commutation" qui, lorsqu'elle est envoyée, utilise une table de sauts pour implémenter une instruction de commutation. Cependant, cela ne fonctionne qu'avec des types entiers (cette question concerne les chaînes).

Pour un petit nombre de chaînes, il est plus efficace pour le compilateur de générer des blocs IF/ELSE que d'utiliser une table de hachage.

Lorsque j'ai initialement remarqué cela, j'ai supposé que, du fait que les blocs IF/ELSE étaient utilisés avec un petit nombre de chaînes, le compilateur effectuait la même transformation pour un grand nombre de chaînes.

C'était faux. 'IMA' a eu la gentillesse de me le signaler (eh bien… il n'était pas gentil à ce sujet, mais il avait raison et j'avais tort, ce qui est la partie la plus importante)

J'ai également formulé une hypothèse sérieuse sur le manque d'instruction "switch" dans MSIL (je me suis dit que s'il existait une primitive switch, pourquoi ne l'utilisait-elle pas avec une table de hachage, il ne devait donc pas y avoir de primitive switch? ...) C'était à la fois faux et incroyablement stupide de ma part. Encore une fois, "IMA" me l'a signalé.

J'ai fait les mises à jour ici car c'est l'article le mieux noté et la réponse acceptée.

Cependant, j'ai fait du Wiki de la communauté parce que je pense que je ne mérite pas le REP parce que j'ai tort Si vous en avez l'occasion, votez pour le message 'ima'.

49
Scott Wisniewski

Trois raisons de préférer le switch:

  • Un compilateur ciblant du code natif peut souvent compiler une instruction switch en une branche conditionnelle plus un saut indirect alors qu’une séquence de ifs nécessite un séquence de branches conditionnelles . En fonction de la densité des cas, de très nombreux articles ont été écrits sur la manière de compiler efficacement les déclarations de cas; certains sont liés depuis le page du compilateur lcc . (Lcc avait l'un des compilateurs les plus innovants pour les commutateurs.)

  • Une instruction switch est un choix parmi des alternatives mutuellement exclusives et la syntaxe du commutateur rend ce flux de contrôle plus transparent pour le programmeur. alors un nid d'instructions if-then-else.

  • Dans certaines langues, notamment ML et Haskell, , le compilateur vérifie si vous avez omis certains cas . Je considère cette fonctionnalité comme l’un des principaux avantages de ML et Haskell. Je ne sais pas si C # peut le faire.

Une anecdote: lors d'une conférence qu'il a prononcée sur un prix d'excellence, j'ai entendu Tony Hoare dire que, parmi toutes les choses qu'il a faites au cours de sa carrière, il était particulièrement fier de trois d'entre elles:

  • Inventer Quicksort
  • Inventer l'instruction switch (que Tony a appelée l'instruction case)
  • Début et fin de sa carrière dans l'industrie

Je ne peux pas imaginer vivre sans switch.

18
Norman Ramsey

En fait, une instruction switch est plus efficace. Le compilateur l’optimisera dans une table de consultation où il ne pourra pas utiliser d’instructions if/else. L'inconvénient est qu'une instruction switch ne peut pas être utilisée avec des valeurs variables.
Vous ne pouvez pas faire:

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

il doit être

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}
14
kemiller2002

Le compilateur va tout optimiser dans le même code avec des différences mineures (Knuth, ça vous dit?).

La différence est qu’une instruction switch est plus propre que quinze, si d’autres instructions sont enchaînées.

Les amis ne laissent pas les amis empiler les déclarations if-else.

14
Will

Je n'ai vu personne d'autre soulever le point (évident?) Selon lequel l'avantage supposé d'efficacité de la déclaration de commutateur dépend du fait que les différents cas sont à peu près également probables. Dans les cas où une (ou plusieurs) des valeurs sont beaucoup plus probables, l'échelle si-alors-sinon peut être beaucoup plus rapide, en s'assurant que les cas les plus courants sont vérifiés en premier:

Donc, par exemple:

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

contre

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

Si x est égal à zéro 90% du temps, le code "if-else" peut être deux fois plus rapide que le code basé sur le commutateur. Même si le compilateur transforme le "commutateur" en une sorte de goto astucieux piloté par une table, il ne sera toujours pas aussi rapide que de simplement vérifier le zéro.

12
Mark Bessey

souvent, cela aura l'air mieux - c'est-à-dire qu'il sera plus facile de comprendre ce qui se passe. Étant donné que les performances seront au mieux extrêmement minimes, la différence la plus importante est la vue du code.

Donc, si le/si semble mieux, utilisez-le, sinon utilisez une instruction switch.

7
gbjbaanb

Sujet secondaire, mais je m'inquiète souvent (et vois plus souvent) des instructions if/else et switch deviennent beaucoup trop volumineuses avec trop de cas. Celles-ci nuisent souvent à la maintenabilité.

Les coupables communs incluent:

  1. Faire trop dans plusieurs déclarations if
  2. Plus de déclarations de cas qu'il est humainement possible d'analyser
  3. Trop de conditions dans l’évaluation if pour savoir ce qui est recherché

Pour réparer:

  1. Extrait de la méthode refactoring.
  2. Utilisez un dictionnaire avec des pointeurs de méthode au lieu d'un cas ou utilisez une IoC pour une configurabilité accrue. Les usines de méthodes peuvent aussi être utiles.
  3. Extraire les conditions à leur propre méthode
4
Chris Brandsma

Si vous utilisez simplement if ou else, la solution de base utilise comparsion? opérateur

(value == value1) ? (type1)do this : (type1)or do this;

Vous pouvez faire la routine ou dans un commutateur

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}
3
Robert W.

L'instruction switch est définitivement la plus rapide, puis si if else if. Il existe des tests de vitesse fournis par BlackWasp

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

--Vérifiez-le

Mais cela dépend beaucoup des possibilités que vous essayez d'expliquer, mais j'essaie d'utiliser une instruction switch autant que possible.

2
sleath

Cela ne répond pas vraiment à votre question, mais étant donné qu'il y aura peu de différence entre les versions compilées, je vous prie instamment d'écrire votre code de la manière qui décrit le mieux vos intentions. Non seulement le compilateur a-t-il une meilleure chance de faire ce que vous attendez, mais il sera plus facile pour d'autres de maintenir votre code.

Si votre intention est de créer une branche dans votre programme en fonction de la valeur d'une variable/attribut, alors une instruction switch représente le mieux cette intention.

Si votre intention est de créer une branche dans votre programme en fonction de différentes variables/attributs/conditions, la chaîne if/else si représente le mieux cette intention.

J'admets que cody a raison quand les gens oublient la commande break, mais presque aussi souvent que je vois des personnes faire des choses compliquées si elles bloquent les {}, alors les lignes qui devraient être dans l'instruction conditionnelle ne le sont pas. C’est l’une des raisons pour lesquelles j toujours inclut {} dans mes déclarations if, même s’il ya une ligne. Non seulement c'est plus facile à lire, mais si j'ai besoin d'ajouter une autre ligne dans le conditionnel, je ne peux pas oublier de l'ajouter.

2
dj_segfault

Selon ce lien, IF vs Switch comparaison du test d'itération à l'aide des instructions switch et if, correspond à 1 000 000 000 d'itérations, Temps mis par Switch Instruction = 43.0s & par Si instruction = 48.0s

Ce qui est littéralement 20833333 itérations par seconde, alors, devrions-nous vraiment nous concentrer davantage,

P.S: Juste pour connaître la différence de performance pour une petite liste de conditions.

2
Bretfort

Question d'intérêt. Cela est arrivé il y a quelques semaines au travail et nous avons trouvé une réponse en écrivant un exemple d'extrait de code et en le visualisant dans .NET Reflector (le réflecteur est génial !! je l'aime).

Voici ce que nous avons découvert: Une instruction switch valide pour toute autre chose qu'une chaîne est compilée en langage IL en tant qu'instruction switch. Toutefois, s’il s’agit d’une chaîne, elle est réécrite sous la forme d’un if/else if/else dans IL. Donc, dans notre cas, nous voulions savoir comment les instructions switch comparent les chaînes, par exemple si elles sont sensibles à la casse, etc. et que le réflecteur nous a rapidement donné une réponse. C'était utile à savoir.

Si vous souhaitez effectuer une comparaison sensible à la casse sur des chaînes, utilisez pourrait une instruction switch, car elle est plus rapide que effectuer une String.Compare dans un if/else. (Edit: Read Quoi de plus rapide, allumer chaîne ou elseif sur type? pour certains tests de performances) Cependant, si vous voulez faire une distinction entre majuscules et minuscules, utilisez de préférence if/else le code résultant n'est pas joli.

switch (myString.ToLower())
{
  // not a good solution
}

La meilleure règle empirique consiste à utiliser des instructions switch si cela a un sens (sérieusement), par exemple:

  • cela améliore la lisibilité de votre code
  • vous comparez une plage de valeurs (float, int) ou une énumération

Si vous devez manipuler la valeur pour alimenter l'instruction switch (créer une variable temporaire contre laquelle basculer), vous devriez probablement utiliser une instruction de contrôle if/else.

Une mise à jour:

En fait, il est préférable de convertir la chaîne en majuscule (par exemple, ToUpper()), car il semble que le compilateur juste-à-temps puisse effectuer des optimisations supplémentaires par rapport au ToLower(). C'est une micro optimisation, mais dans une boucle étroite, cela pourrait être utile.


Une petite note de côté:

Pour améliorer la lisibilité des instructions de commutateur, procédez comme suit:

  • mettre la branche la plus probable en premier, c’est-à-dire la plus consultée
  • s'ils sont tous susceptibles de se produire, listez-les par ordre alphabétique afin de pouvoir les retrouver plus facilement.
  • n'utilisez jamais la fourre-tout par défaut pour la dernière condition restante, car elle est paresseuse et causera des problèmes plus tard dans la vie du code.
  • utilisez la combinaison générique par défaut pour affirmer une condition inconnue même s'il est hautement improbable que cela se produise. c'est ce que prétend être bon.
1
Dennis

Non seulement C #, mais tous les langages basés sur C, je pense: comme un commutateur est limité à des constantes, il est possible de générer un code très efficace en utilisant une "table de saut". Le cas C est vraiment un bon vieux GOTO calculé en FORTRAN, mais le cas C # est toujours testé par rapport à une constante.

L’optimiseur ne pourra pas créer le même code. Considérer, par exemple,

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

Comme il s’agit de booléens composés, le code généré doit calculer une valeur et un court-circuit. Considérons maintenant l'équivalent

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

Ceci peut être compilé dans

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

parce que vous dites implicitement au compilateur qu'il n'a pas besoin de calculer les tests OR et d'égalité.

1
Charlie Martin

Je pense que l'interrupteur est plus rapide que si les conditions telles que voir s'il existe un programme tel que:

Écrivez un programme pour entrer un nombre quelconque (entre 1 et 99) et vérifiez qu'il se trouve dans le logement a) 1 à 9, puis le logement 1 b) 11 - 19, puis le logement 2 c) 21-29, puis le logement 3 et ainsi de suite jusqu'à 89- 99

Puis de suite si vous devez créer de nombreuses conditions, mais vous devez tout simplement taper

Commutateur (no/10)

et sur le cas 0 = 1-9, le cas 1 = 11-19 et ainsi de suite

ce sera si facile

Il y a beaucoup d'autres exemples semblables aussi!

0
user3957230

je sais que ce n’est pas exactement la question pour le moment mais j’ai vraiment besoin de préciser que lorsque vous pensez à l’efficacité à ce niveau, vous aurez peut-être besoin de plus d’abstraction dans votre code. Vous n'aurez plus besoin de changer de boîtier, surtout s'il contient de la logique. (mon exemple en php).

    $feeMapping = [
        1000 => 1,
        2000 => 2,
        3000 => 3,
        4000 => 4,
        5000 => 5,
        6000 => 6,
        7000 => 7
    ];

    function findFee($feeMapping, $amount) {
        foreach ($feeMapping as $fee => $value) {
            if ($value >= $amount) {
                return $fee;
            }
        }

        return 7;
    }

    $feeValue = findFee($feeMapping, 200);

Maintenant, regardez la redondance du code similaire!

    if ($amount >= 1000) {
        return 1;
    } elseif ($amount >= 2000) {
        return 2;
    } elseif ($amount >= 3000) {
        return 3;
    } elseif ($amount >= 4000) {
        return 4;
    } elseif ($amount >= 5000) {
        return 5;
    } elseif ($amount >= 6000) {
        return 6;
    } else {
        return 7;
    }
0
SinisterGlitch

Mon professeur de psychologie m'a suggéré de ne pas changer de déclaration, car les gens oubliaient souvent la pause ou ne l'utilisaient pas correctement. Je ne me souviens pas exactement de ce qu’il a dit, mais quelque chose du genre montre que l’examen d’une base de code séminal qui donnait des exemples de la déclaration de commutateur (il y a des années) comportait aussi une tonne d’erreurs.

0
cody

Un inconvénient possible des déclarations de commutateur est son absence de conditions multiples. Vous pouvez avoir plusieurs conditions pour les instructions if (else) mais pas multiples, avec des conditions différentes dans un commutateur.

Les instructions de commutateur ne conviennent pas aux opérations logiques allant au-delà des équations/expressions booléennes simples. Pour les équations/expressions booléennes, cela convient parfaitement, mais pas pour les autres opérations logiques.

Vous avez beaucoup plus de liberté avec la logique disponible dans les instructions If, mais la lisibilité peut en souffrir si l’instruction If devient trop lourde ou si elle est mal gérée.

Les deux ont leur place en fonction du contexte de ce à quoi vous êtes confronté.

0
Neil Meyer

Ce que je viens de remarquer, c’est que vous pouvez combiner si/d’autre et changer d’instruction! Très utile pour vérifier les conditions préalables.

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}
0
Even Mien

une déclaration de commutateur est fondamentalement une comparaison pour l'égalité. les événements de clavier ont un grand avantage sur les instructions switch quand il est facile d'écrire et de lire du code, une instruction if elseif, le fait de manquer un {support} pourrait également être troublant.

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

Une déclaration if elseif est idéale pour plusieurs solutions si (theAmountOfApples est supérieur à 5 && theAmountOfApples est inférieur à 10) enregistrez vos pommes sinon si (theAmountOfApples est supérieur à 10 || theAmountOfApples == 100) vendez vos pommes. Je n'écris pas c # ou c ++ mais je l'ai appris avant j'ai appris Java et ce sont des langages proches).

0
Geen