it-swarm-pt.tech

Vários casos na instrução switch

Existe uma maneira de cair em várias declarações de caso sem declarar case value: repetidamente?

Eu sei que isso 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;
}

mas eu gostaria de fazer algo assim:

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

É esta sintaxe que eu estou pensando em uma língua diferente, ou estou faltando alguma coisa?

517
theo

Não há sintaxe em C++ nem em C # para o segundo método que você mencionou.

Não há nada errado com o seu primeiro método. Se, no entanto, você tiver intervalos muito grandes, use apenas uma série de instruções if.

282
Brian R. Bondy

Eu acho que isso já foi respondido. No entanto, acho que você ainda pode misturar as duas opções de uma maneira sintaticamente melhor, fazendo:

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
Carlos Quintanilla

Esta sintaxe é do 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

Você não pode usar essa sintaxe em c #. Em vez disso, você deve usar a sintaxe do seu primeiro exemplo.

65
Neal

Um pouco atrasado para a pergunta original, mas estou postando esta resposta na esperança de que alguém usando uma versão mais recente ( C # 7 - disponível por padrão no Visual Studio 2017/.NET Framework 4.6.2 ) , será útil.

No C # 7, a comutação baseada em intervalo é agora possível com a instrução switch e ajudaria com o problema do OP.

Exemplo:

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:

  • Os parênteses ( e ) não são necessários na condição when, mas são usados ​​neste exemplo para destacar as comparações.
  • var também pode ser usado no lugar de int. Por exemplo: case var n when n >= 7:.
47
Steve Gomez

Você pode deixar de fora a nova linha que lhe dá:

case 1: case 2: case 3:
   break;

mas eu considero esse estilo ruim.

31
Allan Wind

O .NET Framework 3.5 tem intervalos:

Enumerable.Range do MSDN

você pode usá-lo com "contém" e a instrução IF, já que alguém disse que a instrução SWITCH usa o operador "==".

Aqui um exemplo:

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

Mas acho que podemos nos divertir mais: já que você não precisará dos valores de retorno e esta ação não requer parâmetros, você pode facilmente usar ações!

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

O antigo exemplo com este novo método:

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

Desde que você está passando ações, não valores, você deve omitir os parênteses, é muito importante. Se você precisar de função com argumentos, basta alterar o tipo de Action para Action<ParameterType>. Se você precisar de valores de retorno, use Func<ParameterType, ReturnType>.

No C # 3.0 não existe um aplicativo fácil Parcial para encapsular o fato de que o parâmetro case é o mesmo, mas você cria um pequeno método auxiliar (um pouco detalhado, tho).

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

Aqui um exemplo de como nova instrução importada funcional é IMHO mais poderoso e elegante do que o antigo imperativo.

18
Luca Molteni

@ Jennifer Owens: você está absolutamente certo o código abaixo não vai funcionar:

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

A única maneira de fazer isso é:

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

O código que você está procurando funciona em visual basic onde você pode facilmente colocar ranges ... em nenhuma opção de switch ou se mais blocos forem convenientes, eu sugiro que, em um ponto muito extremo, faça .dll com visual basic e importe de volta para o seu projeto c #.

Nota: o equivalente no interruptor visual basic é um caso selecionado.

8
none

Outra opção seria usar uma rotina. Se todos os casos 1-3 executam a mesma lógica, envolva essa lógica em uma rotina e chame-a para cada caso. Eu sei que isso não elimina as declarações de caso, mas implementa um bom estilo e mantém a manutenção no mínimo ...

[Editar] Adicionado implementação alternativa para corresponder à pergunta original ... [/ Edit]

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

o gcc implementa uma extensão para a linguagem C para suportar intervalos sequenciais:

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

Editar: Apenas notei a tag C # na pergunta, então presumivelmente uma resposta do gcc não ajuda.

5
DGentry

Aqui está a solução completa do 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;
}

Trabalha com strings também ...

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

Uma faceta menos conhecida de switch em C # é que ele depende do operator = e, como ele pode ser anulado, você pode ter algo assim:


string s = foo();

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

Na verdade eu não gosto do comando GOTO também, mas é em materiais oficiais da MS, aqui estão todas as sintaxes permitidas.

Se o ponto final da lista de instruções de uma seção de comutação for alcançável, ocorrerá um erro de tempo de compilação. Isso é conhecido como a regra "sem queda". O exemplo

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

é válido porque nenhuma seção de opção possui um ponto final acessível. Ao contrário de C e C++, a execução de uma seção de comutação não é permitida para "cair" para a próxima seção de comutador, e o exemplo

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

resulta em um erro em tempo de compilação. Quando a execução de uma seção de switch deve ser seguida pela execução de outra seção de switch, um caso goto explícito ou uma instrução default goto devem ser usados:

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

Vários rótulos são permitidos em uma seção de comutação. O exemplo

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

Eu acredito que neste caso em particular, o GOTO pode ser usado, na verdade é a única maneira de fazer isso.

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

3
Jiří Herník

Uma quantidade enorme de trabalho parece ter sido usada para encontrar maneiras de obter uma das sintaxes menos usadas do C # para, de alguma forma, parecer melhor ou funcionar melhor. Pessoalmente, acho que a instrução switch raramente vale a pena usar. Eu sugiro fortemente analisar quais dados você está testando e os resultados finais que você está querendo.

Vamos dizer, por exemplo, que você deseja testar rapidamente os valores em um intervalo conhecido para ver se eles são números primos. Você quer evitar que seu código faça cálculos desnecessários e pode encontrar uma lista de primos no intervalo que deseja online. Você poderia usar uma instrução switch massiva para comparar cada valor com números primos conhecidos.

Ou você pode simplesmente criar um mapa de matriz de primos e obter resultados imediatos:

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

Talvez você queira ver se um caractere em uma string é hexadecimal. Você poderia usar uma declaração de troca grande e desagradável.

Ou você pode usar expressões regulares para testar o caractere ou usar a função IndexOf para procurar o caractere em uma cadeia de caracteres hexadecimais conhecidos:

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

Vamos dizer que você quer fazer uma das três ações diferentes, dependendo de um valor que será o intervalo de 1 a 24. Eu sugiro usar um conjunto de instruções IF. E se isso for muito complexo (ou os números forem maiores, como 5 ações diferentes, dependendo de um valor no intervalo de 1 a 90), use um enum para definir as ações e criar um mapa de matriz dos enums. O valor então seria usado para indexar no mapa da matriz e obter o enum da ação desejada. Em seguida, use um pequeno conjunto de instruções IF ou uma instrução switch muito simples para processar o valor de enum resultante.

Além disso, o bom sobre um mapa de matriz que converte um intervalo de valores em ações é que ele pode ser facilmente alterado pelo código. Com hard wired code você não pode mudar facilmente o comportamento em tempo de execução, mas com um mapa de array é fácil.

2
Darin

Se você tem uma quantidade muito grande de string (ou qualquer outro tipo) Caso todos façam a mesma coisa, eu recomendo o uso de uma string list combinada com a propriedade string.Contains.

Então, se você tiver uma grande declaração como:

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

Você pode querer substituí-lo por uma declaração 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 bem para qualquer número de caso de cadeia.

1
Maxter