it-swarm.it

Casi multipli nell'istruzione switch

C'è un modo per passare attraverso più case statement senza affermare case value: ripetutamente?

So che questo funziona:

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;
}

ma mi piacerebbe fare qualcosa del genere:

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

È questa sintassi che sto pensando da una lingua diversa, o mi manca qualcosa?

517
theo

Non c'è sintassi in C++ né C # per il secondo metodo che hai citato.

Non c'è niente di sbagliato nel tuo primo metodo. Se tuttavia hai intervalli molto grandi, usa una serie di istruzioni if.

282
Brian R. Bondy

Immagino che questo sia già stato risposto. Tuttavia, penso che puoi ancora mescolare entrambe le opzioni in un modo sintatticamente migliore facendo:

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

Questa sintassi proviene da Visual Basic Seleziona ... 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

Non puoi usare questa sintassi in C #. Invece, devi usare la sintassi del tuo primo esempio.

65
Neal

Un po 'tardi per la domanda originale, ma sto postando questa risposta nella speranza che qualcuno usi una versione più recente ( C # 7 - disponibile per impostazione predefinita in Visual Studio 2017/.NET Framework 4.6.2 ) , lo troverà utile.

In C # 7, la commutazione basata su intervallo è ora possibile con l'istruzione switch e potrebbe aiutare con il problema dell'OP.

Esempio:

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

Note:

  • Le parentesi ( e ) non sono richieste nella condizione when, ma sono utilizzate in questo esempio per evidenziare il/i confronto/i.
  • var può anche essere usato al posto di int. Ad esempio: case var n when n >= 7:.
47
Steve Gomez

Puoi tralasciare la nuova riga che ti dà:

case 1: case 2: case 3:
   break;

ma considero quello stile cattivo.

31
Allan Wind

.NET Framework 3.5 ha intervalli:

Enumerable.Range da MSDN

puoi usarlo con "contiene" e l'istruzione IF, poiché come qualcuno ha detto che l'istruzione SWITCH usa l'operatore "==".

Ecco un esempio:

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

Ma penso che possiamo divertirci di più: dato che non avrai bisogno dei valori di ritorno e questa azione non prende parametri, puoi facilmente usare le azioni!

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

Il vecchio esempio con questo nuovo metodo:

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

Dato che stai passando azioni, non valori, dovresti omettere la parentesi, è molto importante. Se hai bisogno di una funzione con argomenti, cambia il tipo di Action in Action<ParameterType>. Se hai bisogno di valori di ritorno, usa Func<ParameterType, ReturnType>.

In C # 3.0 non esiste una semplice Applicazione parziale per incapsulare il fatto che il parametro case è lo stesso, ma si crea un piccolo metodo di supporto (un po 'prolisso, quindi).

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

Ecco un esempio di come le nuove affermazioni funzionali importate siano IMHO più potenti ed eleganti del vecchio imperativo.

18
Luca Molteni

@ Jennifer Owens: hai perfettamente ragione il codice qui sotto non funzionerà:

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

L'unico modo per farlo è:

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

Il codice che stai cercando funziona su visual basic, dove puoi facilmente inserire intervalli ... in nessuna opzione di switch o se altrimenti blocchi convenienti, ti suggerirei, in un punto estremo, di creare .dll con visual basic e import back al tuo progetto c #.

Nota: switch equivalente in visual basic è select case.

8
none

Un'altra opzione sarebbe quella di utilizzare una routine. Se i casi 1-3 eseguono tutte la stessa logica, avvolgono quella logica in una routine e la chiamano per ciascun caso. So che questo in realtà non elimina le dichiarazioni del caso, ma implementa un buon stile e mantiene la manutenzione al minimo .....

[Modifica] Aggiunta implementazione alternativa per far corrispondere la domanda originale ... [/ Modifica]

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

gcc implementa un'estensione al linguaggio C per supportare intervalli sequenziali:

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

Modificare: Ho appena notato il tag C # sulla domanda, quindi presumibilmente una risposta gcc non aiuta.

5
DGentry

Ecco la soluzione C # 7 completa ...

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;
}

Funziona anche con le stringhe ...

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

Un aspetto meno noto di switch in C # è che si basa sull'operatore = e poiché può essere sovrascritto si potrebbe avere qualcosa di simile a questo:


string s = foo();

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

In realtà non mi piace anche il comando GOTO, ma è nei materiali ufficiali MS, qui ci sono tutte le sintassi consentite.

Se il punto finale dell'elenco di istruzioni di una sezione switch è raggiungibile, si verifica un errore in fase di compilazione. Questa è conosciuta come la regola "no fall through". L'esempio

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

è valido perché nessuna sezione switch ha un punto finale raggiungibile. A differenza di C e C++, l'esecuzione di una sezione di switch non è consentita per "passare attraverso" alla sezione successiva dello switch e all'esempio

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

genera un errore in fase di compilazione. Quando l'esecuzione di una sezione di switch deve essere seguita dall'esecuzione di un'altra sezione di switch, è necessario utilizzare un caso goto esplicito o un'istruzione di default goto:

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

Più etichette sono consentite in una sezione di commutazione. L'esempio

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

Credo che in questo caso particolare, GOTO può essere utilizzato, in realtà è l'unico modo per sfondare.

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

3
Jiří Herník

Un sacco di lavoro sembra essere stato messo alla ricerca di modi per ottenere una delle sintassi meno utilizzate in C # per apparire in qualche modo migliore o funzionare meglio. Personalmente trovo che raramente valga la pena usare l'istruzione switch. Suggerisco caldamente di analizzare quali dati si stanno testando e i risultati finali che si desiderano.

Diciamo per esempio che si desidera testare rapidamente i valori in un intervallo noto per vedere se sono numeri primi. Vuoi evitare che il tuo codice esegua i calcoli inutili e puoi trovare un elenco di numeri primi nell'intervallo che desideri online. È possibile utilizzare un'enorme istruzione switch per confrontare ciascun valore con i numeri primi conosciuti.

O potresti semplicemente creare una mappa di array di numeri primi e ottenere risultati immediati:

    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();
        }
    }

Forse vuoi vedere se un personaggio in una stringa è esadecimale. Potresti usare una dichiarazione switch ungly e piuttosto grande.

Oppure puoi usare entrambe le espressioni regolari per testare il char o usare la funzione IndexOf per cercare il char in una stringa di lettere esadecimali conosciute:

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

Supponiamo che tu voglia fare una delle 3 diverse azioni a seconda di un valore che sarà compreso tra 1 e 24. Suggerirei di utilizzare un insieme di istruzioni IF. E se ciò diventasse troppo complesso (oppure i numeri fossero più grandi, come 5 azioni diverse a seconda di un valore compreso tra 1 e 90), utilizzare un enum per definire le azioni e creare una mappa di array dell'enumerazione. Il valore verrebbe quindi utilizzato per indicizzare la mappa dell'array e ottenere l'enum dell'azione desiderata. Quindi utilizzare un piccolo insieme di istruzioni IF o un'istruzione switch molto semplice per elaborare il valore enum risultante.

Inoltre, la cosa bella di una mappa di array che converte un intervallo di valori in azioni è che può essere facilmente modificata dal codice. Con il codice cablato non è possibile modificare facilmente il comportamento in fase di esecuzione, ma con una mappa di array è facile.

2
Darin

Se si dispone di una quantità molto grande di stringa (o qualsiasi altro tipo) Tutti fanno la stessa cosa, io raccomando l'uso di una lista di stringhe combinata con la proprietà string.Contains.

Quindi, se hai una dichiarazione di switch grande in questo modo:

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;
    .
    .
    .
}

Potresti voler sostituirlo con un'istruzione if come questa:

//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
}

Questa scala va bene per qualsiasi numero di casi stringa.

1
Maxter