it-swarm-es.tech

Múltiples casos en la instrucción switch

¿Hay alguna manera de pasar por varias declaraciones de casos sin indicar case value: repetidamente?

Sé que esto funciona:

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

pero me gustaría hacer algo como esto:

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

¿Estoy pensando en esta sintaxis de un idioma diferente o me estoy perdiendo algo?

517
theo

No hay sintaxis en C++ ni en C # para el segundo método que mencionó.

No hay nada malo con tu primer método. Sin embargo, si tiene rangos muy grandes, solo use una serie de sentencias if.

282
Brian R. Bondy

Supongo que esto ya ha sido respondido. Sin embargo, creo que aún puede combinar ambas opciones de una manera sintácticamente mejor haciendo:

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

Esta sintaxis es 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

No puede utilizar esta sintaxis en C #. En su lugar, debe utilizar la sintaxis de su primer ejemplo.

65
Neal

Un poco tarde para la pregunta original, pero estoy publicando esta respuesta con la esperanza de que alguien use una versión más reciente ( C # 7 - disponible de forma predeterminada en Visual Studio 2017/.NET Framework 4.6.2 ) , lo encontrará útil.

En C # 7, el cambio basado en rango ahora es posible con la instrucción switch y ayudaría con el problema del OP.

Ejemplo:

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

Notas:

  • Los paréntesis ( y ) no se requieren en la condición when, pero se usan en este ejemplo para resaltar las comparaciones.
  • var también puede usarse en lugar de int. Por ejemplo: case var n when n >= 7:.
47
Steve Gomez

Puedes dejar de lado la nueva línea que te da:

case 1: case 2: case 3:
   break;

pero considero ese mal estilo.

31
Allan Wind

.NET Framework 3.5 tiene rangos:

Enumerable. Rango de MSDN

puede usarlo con "contiene" y la instrucción IF, ya que, como alguien dijo, la instrucción SWITCH usa el operador "==".

Aquí un ejemplo:

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

Pero creo que podemos divertirnos más: ya que no necesitará los valores de retorno y esta acción no toma parámetros, ¡puede usar acciones fácilmente!

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

El viejo ejemplo con este nuevo método:

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

Dado que está pasando acciones, no valores, debe omitir el paréntesis, es muy importante. Si necesita una función con argumentos, simplemente cambie el tipo de Action a Action<ParameterType>. Si necesita valores de retorno, use Func<ParameterType, ReturnType>.

En C # 3.0 no es fácil Aplicación parcial para encapsular el hecho de que el parámetro de caso es el mismo, pero creas un pequeño método auxiliar (un poco detallado, aunque).

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

Aquí hay un ejemplo de cómo las nuevas declaraciones funcionales importadas son IMHO más poderosas y elegantes que la imperativa anterior.

18
Luca Molteni

@ Jennifer Owens: tienes toda la razón, el siguiente código no funcionará:

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

La única manera de hacer esto es:

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

El código que busca funciona en Visual Basic, donde puede colocar fácilmente rangos ... en ninguna opción de cambio o, si no, en bloques convenientes, le sugiero, en un punto muy extremo, hacer .dll con Visual Basic e importar de nuevo a tu c # proyecto.

Nota: cambiar el equivalente en Visual Basic es seleccionar caso.

8
none

Otra opción sería utilizar una rutina. Si todos los casos del 1 al 3 ejecutan la misma lógica, envuelva esa lógica en una rutina y llámela para cada caso. Sé que esto no elimina realmente las declaraciones del caso, pero implementa un buen estilo y mantiene el mantenimiento al mínimo ...

[Editar] Se agregó implementación alternativa para que coincida con la pregunta original ... [/ Editar]

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 una extensión al lenguaje C para admitir rangos secuenciales:

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

Editar: Acabo de notar la etiqueta C # en la pregunta, así que probablemente una respuesta de gcc no sea de ayuda.

5
DGentry

Aquí está la solución completa de 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;
}

Funciona también con cuerdas ...

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

Una faceta menos conocida de switch en C # es que se basa en operator = y como puede anularse, podría tener algo como esto:


string s = foo();

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

En realidad, tampoco me gusta el comando GOTO, pero está en los materiales oficiales de MS, aquí están todas las sintaxis permitidas.

Si se puede alcanzar el punto final de la lista de instrucciones de una sección de conmutador, se produce un error en tiempo de compilación. Esto se conoce como la regla de "no caer en". El ejemplo

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

es válido porque ninguna sección de interruptor tiene un punto final alcanzable. A diferencia de C y C++, la ejecución de una sección de conmutación no está permitida para "pasar" a la siguiente sección de conmutación, y el ejemplo

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

da como resultado un error en tiempo de compilación. Cuando la ejecución de una sección de conmutación va a ser seguida por la ejecución de otra sección de conmutación, se debe utilizar un caso goto explícito o una declaración predeterminada de goto:

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

Se permiten múltiples etiquetas en una sección de conmutación. El ejemplo

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

Creo que en este caso particular, se puede usar el GOTO, en realidad es la única forma de lograrlo.

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

3
Jiří Herník

Parece que se ha invertido una gran cantidad de trabajo en la búsqueda de formas para que una de las sintaxis menos utilizadas de C # se vea mejor o funcione mejor. Personalmente me parece que la declaración de cambio rara vez vale la pena usar. Le sugeriría encarecidamente que analice qué datos está probando y los resultados finales que desea.

Digamos, por ejemplo, que desea probar rápidamente los valores en un rango conocido para ver si son números primos. Desea evitar que su código realice los cálculos inútiles y puede encontrar una lista de números primos en el rango que desea en línea. Podría usar una instrucción de cambio masivo para comparar cada valor con números primos conocidos.

O simplemente puede crear un mapa de matriz de números primos y obtener resultados inmediatos:

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

Tal vez quieras ver si un carácter en una cadena es hexadecimal. Podrías usar una declaración de cambio ungly y algo grande.

O puede usar expresiones regulares para probar el carácter o usar la función IndexOf para buscar el carácter en una cadena de letras hexadecimales conocidas:

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

Digamos que desea realizar una de 3 acciones diferentes en función de un valor que estará en el rango de 1 a 24. Le sugiero que utilice un conjunto de declaraciones IF. Y si eso se volvió demasiado complejo (o los números eran más grandes, como 5 acciones diferentes dependiendo de un valor en el rango de 1 a 90), entonces use una enumeración para definir las acciones y crear un mapa matricial de las enumeraciones. El valor se usaría para indexar en el mapa de la matriz y obtener la enumeración de la acción que desea. Luego use un pequeño conjunto de declaraciones IF o una instrucción de cambio muy simple para procesar el valor de enumeración resultante.

Además, lo bueno de un mapa de matriz que convierte un rango de valores en acciones es que se puede cambiar fácilmente mediante un código. Con el código cableado no puede cambiar fácilmente el comportamiento en tiempo de ejecución, pero con un mapa de matriz es fácil.

2
Darin

Si tiene una gran cantidad de cadenas (o cualquier otro tipo), todas hacen lo mismo, recomiendo el uso de una lista de cadenas combinada con la propiedad string.Contains.

Así que si tienes una gran instrucción de cambio como esta:

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

Es posible que desee reemplazarlo con una instrucción if como esta:

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

Esta escala bien para cualquier número de cadenas.

1
Maxter