Múltiples casos en declaración de cambio

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

¿Es esta sintaxis en la que estoy pensando de un idioma diferente o me falta algo?

preguntado el 15 de septiembre de 08 a las 23:09

¿Hay alguna razón por la que no use simplemente una declaración IF (si está verificando un rango de entradas)? -

sí charlse, la primera forma funciona bien, la he usado en numerosos lugares. Está más sucio de lo que me gustaría, pero es útil. Solo usé esos números enteros como ejemplo. Los datos reales fueron más variados. Un if (1 || 2 || 3) {...} else if (4 || 5 || 6) {...} también habría funcionado, pero es más difícil de leer. -

¿Por qué considera que el segundo es más sucio que el primero? Este ltimo agrega otro significado a , y uno que no se comparte con ningún otro lenguaje de estilo C. Eso me parecería mucho más sucio. -

Es posible que haya recogido la sintaxis de la segunda de Ruby. Así es como funciona en ese idioma (aunque el cambio se convierte en caso y el caso se convierte en cuando, entre otras cosas).

Nota importante. Los rangos son compatibles con el caso del conmutador a partir de C # v7; consulte el https://www.youtube.com/watch?v=xB-eutXNUMXJtA&feature=youtu.be -

20 Respuestas

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

No hay nada de malo en tu primer método. Sin embargo, si tiene rangos muy grandes, simplemente use una serie de declaraciones if.

Respondido el 16 de Septiembre de 08 a las 03:09

Como adición, quería agregar un enlace a la especificación del lenguaje C # disponible en MSDN en msdn.microsoft.com/en-us/vcsharp/aa336809.aspx - Richard McGuire

El usuario podría usar algunos if (o una búsqueda de tabla) para reducir la entrada a un conjunto de enumeraciones y activar la enumeración. - Harvey

Creo que esto ya no es correcto. Ver stackoverflow.com/questions/20147879/… . También en esta misma pregunta hay una respuesta. stackoverflow.com/a/44848705/1073157 - dan rayson

Supongo que esto ya ha sido respondido. Sin embargo, creo que aún puede mezclar 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;
}

Respondido 17 Feb 20, 09:02

El código contraído se alarga al primer ejemplo de la pregunta. Bien podría hacerlo como está en la pregunta. - MetalPhoenix

¿Por qué molestarse? El indentador automático en Visual Studio 2013 lo revertirá al formato de la pregunta original de todos modos. - Gustav

@JeremyChild Quizás porque esta respuesta es simplemente una copia disfrazada de la pregunta. Uno de los raros momentos en que estoy rechazando una respuesta. De verdad, ¿cómo consiguió esto tanto apoyo? - T_D

@T_D está recibiendo apoyo porque en realidad responde a la pregunta. El OP dijo, me estoy perdiendo algo ... Carlos respondió con lo que se estaba perdiendo. Me parece bastante cortado y seco. No odies que tenga 422 votos a favor. - mike devenney

@MikeDevenney Luego interpretó la pregunta de manera diferente, por lo que veo, la respuesta correcta sería "no, c # no tiene ninguna sintaxis para eso". Si alguien pregunta "¿es posible verter líquido en un vaso que tengo boca abajo?" la respuesta debería ser "no" y no "puedes verter líquido si lo miras al revés y usas tu imaginación", porque esta respuesta se trata de usar la imaginación. Si usa la sintaxis normal pero la formatea mal, se parece a otra sintaxis, con algo de imaginación. Ojalá entiendas mi punto ...: P - T_D

En C # 7 (disponible de forma predeterminada en Visual Studio 2017 / .NET Framework 4.6.2), el cambio basado en rango ahora es posible con el declaración de cambio 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 son obligatorios en el when condición, pero se utilizan en este ejemplo para resaltar la (s) comparación (es).
  • var también se puede utilizar en lugar de int. Por ejemplo: case var n when n >= 7:.

Respondido 28 Feb 20, 01:02

Esta (coincidencia de patrones) generalmente debería ser la mejor práctica cuando puede usar C # 7.xo superior, ya que es mucho más claro que las otras respuestas. - EternoMedusas

¿Hay alguna forma de lograr esto con una lista de enumeraciones? ¿Dónde se asignan los Enums a int? - Sigex

Esta sintaxis es de Visual Basic Seleccione ... Declaración de caso:

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 usar esta sintaxis en C #. En su lugar, debe utilizar la sintaxis de su primer ejemplo.

respondido 13 nov., 20:10

Puede omitir la nueva línea que le proporciona:

case 1: case 2: case 3:
   break;

pero lo considero de mal estilo.

Respondido 26 ago 15, 16:08

El mal estilo es subjetivo. Prefiero esto porque muestra claramente la intención. - Thomas Phaneuf

.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: dado que no necesitará los valores devueltos 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 case es el mismo, pero crea un pequeño método auxiliar (aunque un poco detallado).

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

Aquí un ejemplo de cómo la nueva declaración funcional importada es en mi humilde opinión más poderosa y elegante que la antigua imperativa.

Respondido 30 Oct 08, 00:10

Buena elección. Sin embargo, una cosa a tener en cuenta: Enumerable.Range tiene argumentos int start y int count. Tus ejemplos no funcionarán de la forma en que fueron escritos. Lo escribe como si el segundo argumento fuera int end. Por ejemplo - Enumerable.Range(11,20) daría como resultado 20 números que comienzan con 11, y no números del 11 al 20. - gabriel macadams

aunque, si trabaja con un Enum, ¿por qué no algo así? if (Enumerable.Range (MyEnum.A, MyEnum.M) {DoThing ();} else if (Enumerable.Range (MyEnum.N, MyEnum.Z) {DoAnotherThing ();} - David Hollowell - MSFT

Tenga en cuenta que Enumerable.Range(11,20).Contains(c) es equivalente a for(int i = 11; i < 21; ++i){ if (i == c) return true; } return false; Si tuvieras un rango grande, llevaría mucho tiempo, mientras solo usas > y < sería rápido y constante. - jon hanna

Una mejora: tener MySwitchWithEnumerable retorno void es un diseño débil para esta situación. MOTIVO: Ha convertido un if-else a una serie de declaraciones independientes, que oculta la intención, que es que son mutuamente excluyentes, solo una action es ejecutado. En lugar de regresar bool, con cuerpo if (..) { action(); return true; } else return false; El sitio que llama muestra la intención: if (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));. Es preferible. Sin embargo, tampoco es una mejora significativa con respecto a su versión original, para este simple caso. - Fabricante de herramientasSteve

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

También funciona con cuerdas ...

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

Respondido 28 Feb 20, 01:02

Esto significaría que asigna las matrices con cada declaración de cambio, ¿verdad? ¿No sería mejor si los tuviéramos como variables constantes? - MaLiN2223

Elegante, pero de hecho sería bueno saber si el compilador optimiza este escenario para que las invocaciones repetidas no incurran en la sobrecarga de la construcción del arreglo cada vez; definir las matrices con anticipación es una opción, pero quita gran parte de la elegancia. - mklement0

El código de abajo no se trabajo:

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

La única forma de hacerlo es:

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

El código que está buscando funciona en Visual Basic, donde puede colocar fácilmente rangos ... en el none opción del switch declaración o if else bloques convenientes, sugeriría, en un punto muy extremo, hacer .dll con Visual Basic e importar de nuevo a su proyecto C #.

Nota: el conmutador equivalente en Visual Basic es Select Case.

Respondido 28 Feb 20, 01:02

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

[Editar] Se agregó una 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()
{
   ...
}

otro

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

private void DoSomething()
{
   ...
}

Respondido el 16 de Septiembre de 08 a las 04:09

Una faceta menos conocida de cambiar en C # es que se basa en el operador = y dado que se puede anular, podría tener algo como esto:


string s = foo();

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

Respondido el 16 de Septiembre de 08 a las 03:09

esto podría convertirse en un gran problema más adelante para otra persona que intente leer el código: andres harry

gcc implementa una extensión del 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, por lo que presumiblemente una respuesta gcc no ayuda.

Respondido el 16 de Septiembre de 08 a las 03:09

En C # 7 ahora tenemos La coincidencia de patrones para que puedas hacer algo como:

switch (age)
{
  case 50:
    ageBlock = "the big five-oh";
    break;
  case var testAge when (new List<int>()
      { 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 }).Contains(testAge):
    ageBlock = "octogenarian";
    break;
  case var testAge when ((testAge >= 90) & (testAge <= 99)):
    ageBlock = "nonagenarian";
    break;
  case var testAge when (testAge >= 100):
    ageBlock = "centenarian";
    break;
  default:
    ageBlock = "just old";
    break;
}

Respondido el 05 de Septiembre de 19 a las 10:09

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

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

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

es válido porque ninguna sección del interruptor tiene un punto final alcanzable. A diferencia de C y C ++, la ejecución de una sección de cambio no puede "pasar" a la siguiente sección de cambio, 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 cambio debe ir seguida de la ejecución de otra sección de cambio, se debe usar 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 varias etiquetas en una sección de interruptores. 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 GOTO, y en realidad es la única forma de fallar.

Fuente

respondido 13 nov., 20:10

Tenga en cuenta que en la práctica, goto casi siempre se puede evitar (aunque no lo considero "terrible" aquí, está cumpliendo un rol específico y estructurado). En su ejemplo, debido a que ha envuelto los cuerpos del caso en funciones (algo bueno), el caso 0 puede convertirse CaseZero(); CaseZeroOrOne(); break;. En goto requerido. - Fabricante de herramientasSteve

Parece que se ha trabajado mucho para encontrar formas de hacer que una de las sintaxis menos utilizadas de C # se vea mejor o funcione mejor de alguna manera. Personalmente, creo que rara vez vale la pena usar la declaración de cambio. Sugeriría encarecidamente analizar qué datos está probando y los resultados finales que desea.

Digamos, por ejemplo, que desea probar rápidamente valores en un rango conocido para ver si son números primos. Desea evitar que su código haga cálculos innecesarios y puede encontrar una lista de números primos en el rango que desea en línea. Podría usar una declaración de cambio masiva 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();
        }
    }

Quizás quieras ver si un carácter de una cadena es hexadecimal. Podría usar una declaración de cambio desagradable 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 {
        }
    }

Supongamos que desea realizar una de 3 acciones diferentes dependiendo de un valor que estará en el rango de 1 a 24. Sugeriría usar 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), use una enumeración para definir las acciones y crear un mapa de matriz de las enumeraciones. Luego, el valor se usaría para indexar en el mapa de matriz y obtener la enumeración de la acción que desea. Luego use un pequeño conjunto de instrucciones IF o una instrucción switch 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 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.

Respondido 23 Feb 12, 06:02

También puede asignar una expresión lambda o un delegado: Conrado Frix

Buenos puntos. Un comentario menor: por lo general, me resulta más fácil mantener una lista de los valores que coinciden con un caso dado, que un mapa de matriz. El problema con el mapa de matrices es que es fácil cometer un error. Por ejemplo, en lugar del mapa de matrices de primes de verdadero / falso, simplemente tenga una lista de primos y cárguelos en un HashSet para el rendimiento de búsqueda. Incluso si hay más de dos casos, por lo general todos menos uno es una lista pequeña, así que cree un HashSet de enumeraciones (si es escaso) o un mapa de matriz, en código, a partir de listas de los otros casos. - Fabricante de herramientasSteve

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

Entonces, si tiene una declaración de cambio grande 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 un if declaración 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 es adecuada para cualquier número de casos de cuerdas.

Respondido 28 Feb 20, 01:02

Creo que este es mejor en C # 7 o superior.

switch (value)
{
    case var s when new[] { 1,2 }.Contains(s):
    // Do something
     break;

    default:
    // Do the default
    break;
 }

También puede verificar el rango en el caso del interruptor C #: Cambiar caso: ¿puedo usar un rango en lugar de un número único? O si desea comprender los conceptos básicos de Caso del interruptor de C #

Respondido 24 Abr '20, 15:04

Solo para agregar a la conversación, usando .NET 4.6.2 también pude hacer lo siguiente. Probé el código y funcionó para mí.

También puede hacer varias declaraciones "O", como a continuación:

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

También puede verificar si coincide con un valor en una matriz:

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

Respondido 03 Jul 19, 01:07

¿No depende esto de la versión C #, no de la versión .NET? - Pedro Mortensen

Con C # 9 vino la coincidencia de patrones relacionales. Esto nos permite hacer:

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

En el tutorial profundo de Relational Patter en C # 9

Cambios de coincidencia de patrones para C # 9.0

Los patrones relacionales permiten al programador expresar que un valor de entrada debe satisfacer una restricción relacional cuando se compara con un valor constante.

Respondido el 23 de enero de 21 a las 20:01

También puede tener condiciones que son completamente diferentes

            bool isTrue = true;

            switch (isTrue)
            {
                case bool ifTrue when (ex.Message.Contains("not found")):
                case bool ifTrue when (thing.number = 123):
                case bool ifTrue when (thing.othernumber != 456):
                    response.respCode = 5010;
                    break;
                case bool ifTrue when (otherthing.text = "something else"):
                    response.respCode = 5020;
                    break;
                default:
                    response.respCode = 5000;
                    break;
            }

Respondido 10 Feb 21, 14:02

Para esto, usaría una declaración goto. Como:

    switch(value){
    case 1:
        goto case 3;
    case 2:
        goto case 3;
    case 3:
        DoCase123();
    //This would work too, but I'm not sure if it's slower
    case 4:
        goto case 5;
    case 5:
        goto case 6;
    case 6:
        goto case 7;
    case 7:
        DoCase4567();
    }

Respondido 09 Jul 11, 23:07

@scone goto rompe un principio fundamental de la programación procedimental (del cual c ++ y c # todavía están arraigados; no son lenguajes OO puros (gracias a Dios)). La programación por procedimientos tiene un flujo lógico bien definido determinado por las construcciones del lenguaje y las convenciones de llamada a métodos (cómo crece y se reduce la pila en tiempo de ejecución). La declaración goto elude este flujo al permitir saltos arbitrarios, básicamente. - Sam es

No digo que sea de buen estilo, persay, pero hace lo que pedía la pregunta original. - bollo

No, no "hace lo que pedía la pregunta original". La pregunta original tenía código que funcionó como está. No necesitaban que lo arreglaran. E incluso si lo hicieran, esta es una sugerencia horrible. Su menos conciso y utiliza goto. Peor aún, es un uso completamente innecesario de goto, ya que funciona la sintaxis original indicada por OP. La pregunta era si había una mas conciso forma de dar los casos alternativos. Como la gente respondió años antes que tú, sí lo hay, si está dispuesto a poner los varios casos en una línea case 1: case 2:y si el estilo automático del editor lo permite. - Fabricante de herramientasSteve

La única razón por la que se determina que los goto son malos es porque a algunas personas les resulta difícil seguir el flujo lógico. .Net MSIL (código de objeto ensamblado) usa goto en todas partes porque es rápido, pero si el código .Net se puede escribir y tener el mismo rendimiento sin ellos, es mejor no usarlos para que no te llame la atención gente como @ La condescendiente respuesta de ToolmakerSteve. - Dynamiclynk

@wchoward: lea mi respuesta con más atención. Mi queja no es solo sobre el uso de Goto. Me opuse porque la pregunta mostró código que ya funciona como está, y esta respuesta a) toma ese código de trabajo y lo hace más detallado y menos estructurado, sin beneficio, b) no responde la pregunta. - Fabricante de herramientasSteve

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.