Несколько случаев в инструкции switch

есть ли способ пройти через несколько операторов case без указания case value: неоднократно?

Я знаю, что это работает:

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

но я хотел бы сделать что-то вроде этого:

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

это синтаксис, о котором я думаю с другого языка, или я что-то упускаю?

14 ответов


нет синтаксиса в C++ или C# для второго метода, который вы упомянули.

нет ничего плохого в вашем первом методе. Если, однако, у вас очень большие диапазоны, просто используйте серию операторов if.


Я думаю, на это уже ответили. Тем не менее, я думаю, что вы все равно можете смешать оба варианта синтаксически лучше, выполнив:

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

этот синтаксис из Visual Basic выбрать...Оператор Case:

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

вы не можете использовать этот синтаксис в C#. Вместо этого необходимо использовать синтаксис из первого примера.


немного поздно для исходного вопроса, но я публикую этот ответ в надежде, что кто-то использует более новую версию (C# 7 -- доступно по умолчанию в Visual Studio 2017 / .NET Framework 4.6.2), найдет это полезным.

в C# 7 переключение на основе диапазона теперь возможно с помощью переключатель и поможет с OP проблема.

пример:

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

Примечания:

  • скобки ( и ) не требуются в when условие, но используются в этом примере, чтобы выделить сравнение(ы).
  • var может также использоваться вместо int. Например: case var n when n >= 7:.

вы можете опустить новую строку, которая дает вам:

case 1: case 2: case 3:
   break;

но я считаю, что это плохой стиль.


.NET Framework 3.5 имеет диапазоны:

перечисли.Диапазон от MSDN

вы можете использовать его с" contains "и оператором IF, так как, как кто-то сказал, оператор SWITCH использует оператор"==".

вот пример:

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

но я думаю, что мы можем получить больше удовольствия: поскольку вам не нужны возвращаемые значения, и это действие не принимает параметров, вы можете легко использовать действия!

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

старый пример с этот новый метод:

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

поскольку вы передаете действия, а не значения, вы должны опустить скобки, это очень важно. Если вам нужна функция с аргументами, просто измените тип Action до Action<ParameterType>. Если вам нужны возвращаемые значения, используйте Func<ParameterType, ReturnType>.

в C# 3.0 нет просто Частичное Применение чтобы инкапсулировать тот факт, что параметр case одинаковый, но вы создаете небольшой вспомогательный метод (немного подробный, tho).

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

здесь пример того, как новый функциональный импортированный оператор IMHO более мощный и элегантный, чем старый императивный.


@ Jennifer Owens: вы абсолютно правы код ниже не будет работать:

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

единственный способ сделать это:

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

код, который вы ищете, работает на visual basic, где вы легко можете поместить диапазоны... ни в одном варианте переключателя или если еще блоки удобны, я бы предложил, в очень экстремальной точке, сделать .dll с visual basic и импортировать обратно в проект c#.

Примечание: эквивалент переключателя в visual basic-select case.


другой вариант - использовать процедуру. Если все случаи 1-3 выполняют одну и ту же логику, то оберните эту логику в подпрограмму и вызовите ее для каждого случая. Я знаю, что это на самом деле не избавляет от операторов case, но он реализует хороший стиль и сводит обслуживание к минимуму.....

[Edit] добавлена альтернативная реализация для соответствия исходному вопросу...[/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()
{
   ...
}

один менее известный аспект переключатель В C# это то, что он полагается на оператор= и поскольку он может быть переопределен, у вас может быть что-то вроде этого:


string s = foo();

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

gcc реализует расширение языка C для поддержки последовательных диапазонов:

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

редактировать: просто заметил тег C# на вопрос, поэтому, по-видимому, ответ gcc не помогает.


на самом деле мне тоже не нравится команда GOTO, но это в официальных материалах MS, здесь все разрешенные синтаксисы.

если конечная точка списка операторов раздела коммутатора достижима, возникает ошибка времени компиляции. Это правило известно как правило "не проваливаться". Пример

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

допустимо, потому что ни один раздел коммутатора не имеет достижимой конечной точки. В отличие от C и c++, выполнение раздела коммутатора не разрешается "проваливаться" в следующий раздел коммутатора, и пример

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

приводит к ошибке времени компиляции. Когда выполнение секции коммутатора должно сопровождаться выполнением другой секции коммутатора, должен использоваться явный случай goto или оператор goto по умолчанию:

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

множественные ярлыки позволены в переключател-разделе. Пример

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

Я считаю, что в этом конкретном случае GOTO можно использовать, это на самом деле единственный способ fallthrough.

источник: http://msdn.microsoft.com/en-us/library/aa664749%28v=vs.71%29.aspx


Кажется, что очень много работы было вложено в поиск способов заставить один из наименее используемых синтаксисов C# выглядеть лучше или работать лучше. Лично я считаю, что оператор switch редко стоит использовать. Я сильно предложил бы анализируя данные тестирования и конечный результат вы хотите.

скажем, например, вы хотите быстро проверить значения в известном диапазоне, чтобы увидеть, если они являются простыми числами. Вы хотите избежать того, чтобы ваш код делал расточительное вычисления и вы можете найти список простых чисел в диапазоне, который вы хотите в интернете. Вы можете использовать массивный оператор switch для сравнения каждого значения с известными простыми числами.

или вы можете просто создать карту массива простых чисел и получить немедленные результаты:

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

возможно, вы хотите увидеть, является ли символ в строке шестнадцатеричным. Вы можете использовать неуклюжий и несколько большой оператор switch.

или вы можете использовать регулярные выражения для проверки char или использовать функция IndexOf для поиска символа в строке известных шестнадцатеричных букв:

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

предположим, вы хотите сделать одно из 3 различных действий в зависимости от значения, которое будет в диапазоне от 1 до 24. Я бы предложил использовать набор операторов IF. И если это стало слишком сложным (или числа были больше, например, 5 различных действий в зависимости от значения в диапазоне от 1 до 90), используйте перечисление для определения действий и создания карты массива перечислений. Значение будет затем используйте для индексирования в карту массива и получения перечисления нужного действия. Затем используйте небольшой набор операторов IF или очень простой оператор switch для обработки результирующего значения перечисления.

кроме того, хорошая вещь о карте массива, которая преобразует диапазон значений в действия, заключается в том, что ее можно легко изменить кодом. С жестким проводным кодом вы не можете легко изменить поведение во время выполнения, но с картой массива это легко.


Если у вас очень большое количество строк (или любого другого типа), все делают то же самое, я рекомендую использовать список строк в сочетании со строкой.Содержит свойство.

Итак, если у вас есть большой оператор switch, например:

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

вы можете заменить его оператором if следующим образом:

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

этот масштаб хорошо подходит для любого количества строковых случаев.


для этого вы будете использовать оператор goto. Например:

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