web-dev-qa-db-fra.com

Plusieurs cas dans l'instruction switch

Existe-t-il un moyen de passer en revue plusieurs déclarations de cas sans indiquer case value: à plusieurs reprises?

Je sais que ça marche:

switch (value)
{
   case 1:
   case 2:
   case 3:
      //do some stuff
      break;
   case 4:
   case 5:
   case 6:
      //do some different stuff
      break;
   default:
       //default stuff
      break;
}

mais j'aimerais faire quelque chose comme ça:

switch (value)
{
   case 1,2,3:
      //Do Something
      break;
   case 4,5,6:
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

Est-ce que cette syntaxe à laquelle je pense est d'une autre langue ou est-ce que quelque chose me manque?

536
theo

Il n'y a pas de syntaxe en C++ ni en C # pour la deuxième méthode que vous avez mentionnée.

Il n'y a rien de mal avec votre première méthode. Si toutefois vous avez de très grandes plages, utilisez simplement une série d'instructions if.

293
Brian R. Bondy

Je suppose que cela a déjà été répondu. Cependant, je pense que vous pouvez toujours combiner les deux options de manière syntaxiquement meilleure en faisant:

switch (value)
{
case 1: case 2: case 3:          
    // Do Something
    break;
case 4: case 5: case 6: 
    // Do Something
    break;
default:
    // Do Something
    break;
}
658
Carlos Quintanilla

Cette syntaxe provient de Visual Basic Select ... Case Statement :

Dim number As Integer = 8
Select Case number
    Case 1 To 5
        Debug.WriteLine("Between 1 and 5, inclusive")
        ' The following is the only Case clause that evaluates to True.
    Case 6, 7, 8
        Debug.WriteLine("Between 6 and 8, inclusive")
    Case Is < 1
        Debug.WriteLine("Equal to 9 or 10")
    Case Else
        Debug.WriteLine("Not between 1 and 10, inclusive")
End Select

Vous ne pouvez pas utiliser cette syntaxe en C #. Au lieu de cela, vous devez utiliser la syntaxe de votre premier exemple.

70
Neal

Un peu en retard pour la question initiale, mais je poste cette réponse dans l'espoir qu'une personne utilisant une version plus récente ( C # 7 - disponible par défaut dans Visual Studio 2017/.NET Framework 4.6 .2 ), le trouvera utile.

En C # 7, la commutation basée sur la plage est maintenant possible avec instruction de commutateur et aiderait à résoudre le problème du PO.

Exemple:

int i = 5;

switch (i)
{
    case int n when (n >= 7):
        Console.WriteLine($"I am 7 or above: {n}");
        break;

    case int n when (n >= 4 && n <= 6 ):
        Console.WriteLine($"I am between 4 and 6: {n}");
        break;

    case int n when (n <= 3):
        Console.WriteLine($"I am 3 or less: {n}");
        break;
}

// Output: I am between 4 and 6: 5

Notes:

  • Les parenthèses ( et ) ne sont pas requises dans la condition when, mais sont utilisées dans cet exemple pour mettre en évidence la ou les comparaisons.
  • var peut également être utilisé à la place de int. Par exemple: case var n when n >= 7:.
51
Steve Gomez

Vous pouvez laisser la nouvelle ligne qui vous donne:

case 1: case 2: case 3:
   break;

mais je considère ce mauvais style.

31
Allan Wind

.NET Framework 3.5 a des plages:

Enumerable.Range from MSDN

vous pouvez l'utiliser avec "contient" et l'instruction IF, car, comme quelqu'un l'a dit, l'instruction SWITCH utilise l'opérateur "==".

Voici un exemple:

int c = 2;
if(Enumerable.Range(0,10).Contains(c))
    DoThing();
else if(Enumerable.Range(11,20).Contains(c))
    DoAnotherThing();

Mais je pense que nous pouvons avoir plus de plaisir: puisque vous n’avez pas besoin des valeurs de retour et que cette action ne prend pas de paramètres, vous pouvez facilement utiliser des actions!

public static void MySwitchWithEnumerable(int switchcase, int startNumber, int endNumber, Action action)
{
    if(Enumerable.Range(startNumber, endNumber).Contains(switchcase))
        action();
}

L'ancien exemple avec cette nouvelle méthode:

MySwitchWithEnumerable(c, 0, 10, DoThing);
MySwitchWithEnumerable(c, 10, 20, DoAnotherThing);

Puisque vous transmettez des actions, pas des valeurs, vous devez omettre la parenthèse, c’est très important. Si vous avez besoin d’une fonction avec arguments, modifiez simplement le type de Action en Action<ParameterType>. Si vous avez besoin de valeurs de retour, utilisez Func<ParameterType, ReturnType>.

En C # 3.0, il n’est pas facile Application partielle d’encapsuler le fait que le paramètre case est le même, mais vous créez une petite méthode d’aide (un peu commentée, tho).

public static void MySwitchWithEnumerable(int startNumber, int endNumber, Action action){ 
    MySwitchWithEnumerable(3, startNumber, endNumber, action); 
}

Voici un exemple de la façon dont la nouvelle déclaration importée fonctionnelle est plus puissante et élégante que l’ancien impératif.

18
Luca Molteni

@ Jennifer Owens: vous avez absolument raison, le code ci-dessous ne fonctionnera pas:

case 1 | 3 | 5:
//not working do something

La seule façon de faire est:

case 1: case 2: case 3:
// do something
break;

Le code que vous recherchez fonctionne avec Visual Basic où vous pouvez facilement mettre des plages ... sans option de commutateur ni de bloc pratique, je suggérerais, à un point extrême, de créer un fichier .dll avec Visual Basic et de le réimporter. à votre projet c #.

Remarque: changer d’équivalent dans Visual Basic est un cas choisi.

8
none

Une autre option serait d'utiliser une routine. Si les cas 1 à 3 exécutent tous la même logique, enroulez cette logique dans une routine et appelez-la pour chaque cas. Je sais que cela ne supprime pas réellement les déclarations de cas, mais il met en œuvre un bon style et minimise la maintenance .....

[Modifier] Ajout d'une implémentation alternative pour correspondre à la question d'origine ... [/ Modifier]

switch (x)
{
   case 1:
      DoSomething();
      break;
   case 2:
      DoSomething();
      break;
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}

Alt

switch (x)
{
   case 1:
   case 2:
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}
7
Dr8k

Voici la solution complète C # 7 ...

switch (value)
{
   case var s when new[] { 1,2,3 }.Contains(s):
      //Do Something
      break;
   case var s when new[] { 4,5,6 }.Contains(s):
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

Fonctionne aussi avec des cordes ...

switch (mystring)
{
   case var s when new[] { "Alpha","Beta","Gamma" }.Contains(s):
      //Do Something
      break;
...
}
6
Carter Medlin

gcc implémente une extension du langage C pour prendre en charge des plages séquentielles:

switch (value)
{
   case 1...3:
      //Do Something
      break;
   case 4...6:
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

Modifier: Je viens de remarquer le tag C # sur la question, donc une réponse gcc n’aide en rien.

5
DGentry

Une facette moins connue de switch en C # est qu’elle repose sur le opérateur = et, comme elle peut être annulée, vous pourriez avoir quelque chose comme ceci:


string s = foo();

switch (s) {
  case "abc": /*...*/ break;
  case "def": /*...*/ break;
}
5
Cyber Oliveira

En fait, je n'aime pas trop la commande GOTO, mais c'est dans les documents officiels de MS, voici toutes les syntaxes autorisées.

Si le point final de la liste d'instructions d'une section switch est accessible, une erreur de compilation se produit. Ceci est connu sous le nom de règle "pas de chute". L'exemple

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
default:
   CaseOthers();
   break;
}

est valide car aucune section de commutateur n'a de point final accessible. Contrairement à C et C++, l’exécution d’une section de commutateur n’est pas autorisée à "passer" à la section de commutateur suivante;

switch (i) {
case 0:
   CaseZero();
case 1:
   CaseZeroOrOne();
default:
   CaseAny();
}

résulte en une erreur de compilation. Lorsque l'exécution d'une section de commutateur doit être suivie de l'exécution d'une autre section de commutateur, une instruction goto explicite ou une instruction goto par défaut doivent être utilisées:

switch (i) {
case 0:
   CaseZero();
   goto case 1;
case 1:
   CaseZeroOrOne();
   goto default;
default:
   CaseAny();
   break;
}

Plusieurs étiquettes sont autorisées dans une section de commutation. L'exemple

switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
case 2:
default:
   CaseTwo();
   break;
}

Je crois que dans ce cas particulier, le GOTO peut être utilisé, c’est en fait le seul moyen de réussir.

source: http://msdn.Microsoft.com/en-us/library/aa664749%28v=vs.71%29.aspx

3
Jiří Herník

Il semble que beaucoup de travail ait été fait pour trouver le moyen de rendre l’une des syntaxes les moins utilisées de C # plus esthétique ou plus efficace. Personnellement, je trouve que l'instruction switch vaut rarement la peine d'être utilisée. Je vous suggère fortement d'analyser les données que vous testez et les résultats finaux que vous souhaitez.

Supposons par exemple que vous souhaitiez tester rapidement des valeurs dans une plage connue pour voir s’il s’agit de nombres premiers. Vous voulez éviter que votre code fasse des calculs inutiles et vous pouvez trouver une liste de nombres premiers dans la plage que vous voulez en ligne. Vous pouvez utiliser une instruction switch massive pour comparer chaque valeur à des nombres premiers connus.

Ou vous pouvez simplement créer une carte matricielle de nombres premiers et obtenir des résultats immédiats:

    bool[] Primes = new bool[] {
        false, false, true, true, false, true, false,    
        true, false, false, false, true, false, true,
        false,false,false,true,false,true,false};
    private void button1_Click(object sender, EventArgs e) {
        int Value = Convert.ToInt32(textBox1.Text);
        if ((Value >= 0) && (Value < Primes.Length)) {
            bool IsPrime = Primes[Value];
            textBox2.Text = IsPrime.ToString();
        }
    }

Vous voulez peut-être voir si un caractère dans une chaîne est hexadécimal. Vous pouvez utiliser une instruction switch ungly et un peu grande.

Vous pouvez également utiliser des expressions régulières pour tester le caractère ou utiliser la fonction IndexOf pour rechercher le caractère dans une chaîne de lettres hexadécimales connues:

        private void textBox2_TextChanged(object sender, EventArgs e) {
        try {
            textBox1.Text = ("0123456789ABCDEFGabcdefg".IndexOf(textBox2.Text[0]) >= 0).ToString();
        } catch {
        }
    }

Supposons que vous souhaitiez effectuer l'une des 3 actions différentes en fonction d'une valeur comprise entre 1 et 24. Je suggérerais d'utiliser un ensemble d'instructions IF. Et si cela devenait trop complexe (ou que les nombres étaient plus grands, tels que 5 actions différentes dépendant d'une valeur comprise entre 1 et 90), utilisez une énumération pour définir les actions et créer une carte matricielle des énumérations. La valeur serait ensuite utilisée pour indexer dans la carte du tableau et obtenir l'énumération de l'action souhaitée. Utilisez ensuite un petit ensemble d'instructions IF ou une instruction switch très simple pour traiter la valeur enum résultante.

En outre, l’avantage d’une carte de matrice qui convertit une plage de valeurs en actions est qu’elle peut être facilement modifiée par le code. Avec du code câblé, vous ne pouvez pas facilement changer de comportement au moment de l'exécution, mais avec une carte matricielle, c'est facile.

2
Darin

Si vous avez une très grande quantité de chaîne (ou de tout autre type) Case qui font tous la même chose, je vous recommande d'utiliser une liste de chaînes combinée à la propriété string.Contains.

Donc, si vous avez une grosse instruction switch comme ceci:

switch (stringValue)
{
    case "cat":
    case "dog":
    case "string3":
    ...
    case "+1000 more string": //Too many string to write a case for all!
        //Do something;
    case "a lonely case"
        //Do something else;
    .
    .
    .
}

Vous voudrez peut-être le remplacer par une instruction if comme ceci:

//Define all the similar "case" string in a List
List<string> listString = new List<string>(){ "cat", "dog", "string3", "+1000 more string"};
//Use string.Contains to find what you are looking for
if (listString.Contains(stringValue))
{
    //Do something;
}
else
{
    //Then go back to a switch statement inside the else for the remaining cases if you really need to
}

Cette échelle bien pour n'importe quel nombre de cas de chaîne.

1
Maxter

Pour ajouter à la conversation, en utilisant .NET 4.6.2, j’ai également été capable de faire ce qui suit. J'ai testé le code et cela a fonctionné pour moi.

Vous pouvez également faire plusieurs déclarations "OU", comme ci-dessous:

            switch (value)
            {
                case string a when a.Contains("text1"):
                    // Do Something
                    break;
                case string b when b.Contains("text3") || b.Contains("text4") || b.Contains("text5"):
                    // Do Something else
                    break;
                default:
                    // Or do this by default
                    break;
            }

Vous pouvez également vérifier si elle correspond à une valeur dans un tableau:

            string[] statuses = { "text3", "text4", "text5"};

            switch (value)
            {
                case string a when a.Contains("text1"):
                    // Do Something
                    break;
                case string b when statuses.Contains(value):                        
                    // Do Something else
                    break;
                default:
                    // Or do this by default
                    break;
            }
0
JeffS