Несколько случаев в выражении switch

Есть ли способ пролистать несколько операторов без 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;
}

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

16.09.2008 01:34:41
Есть ли причина, по которой вы не просто используете оператор IF (если вы проверяете диапазон целых чисел)?
Eric Schoonover 16.09.2008 01:39:41
да, Чарльз, первый способ работает хорошо, я использовал его во многих местах. Это грязнее, чем хотелось бы, но полезно. Я просто использовал эти целые числа в качестве примера. Реальные данные были более разнообразными. Если бы (1 || 2 || 3) {...} иначе, если (4 || 5 || 6) {...} тоже бы сработало, но его сложнее читать.
theo 16.09.2008 04:27:30
почему вы считаете последнее более грязным, чем первое. Последний добавляет еще одно значение ,и тот, который не используется ни в каком другом языке c-style. Это показалось бы мне намного грязнее.
Jon Hanna 1.10.2010 14:04:51
Возможно, вы взяли второй синтаксис от Ruby. Вот как это работает на этом языке (хотя переключение становится регистром, а регистр становится, когда, между прочим.)
juanpaco 12.12.2012 16:00:54
Важное примечание . Диапазоны поддерживаются в случае коммутатора, начиная с C # v7 - см. Ответ
RBT 18.05.2018 11:47:27
17 ОТВЕТОВ
РЕШЕНИЕ

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

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

311
16.09.2008 02:30:06
В качестве дополнения я хотел добавить ссылку на спецификацию языка C #, доступную в MSDN по адресу msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
Richard McGuire 16.09.2008 02:20:17
Пользователь может использовать некоторые if (или просмотр таблицы), чтобы уменьшить ввод до набора перечислений и включить перечисление.
Harvey 28.07.2013 20:00:01
вероятно выбрал это из VB.net
George Birbilis 3.10.2016 00:06:47
VB.net лучше подходит для различных операторов case, за исключением того, что они не пропускаются, как в C #. Возьми немного, дай немного.
Brain2000 31.01.2018 00:25:28

Этот синтаксис взят из выражения Visual Basic Select ... 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 #. Вместо этого вы должны использовать синтаксис из вашего первого примера.

75
8.03.2017 12:27:57
это одна из немногих вещей, которые мне не хватает в * Basic.
nickf 16.10.2008 01:57:22
Здесь у нас есть один из немногих дисплеев, где Visual Basic не так страшен и более универсален, чем C #. Это ценный пример!
bgmCoder 14.04.2013 01:09:46
Это довольно прилично. Интересно, почему это не было добавлено в C # 8.0. Было бы неплохо.
Sigex 12.12.2018 11:18:57

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


string s = foo();

switch (s) {
  case "abc": /*...*/ break;
  case "def": /*...*/ break;
}
5
16.09.2008 02:16:25
позже это может стать большой ошибкой для кого-то другого, пытающегося прочитать код
Andrew Harry 12.07.2011 03:45:49

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

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

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

5
16.09.2008 02:59:52

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

case 1: case 2: case 3:
   break;

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

32
26.08.2015 15:11:03

Другим вариантом будет использование рутины. Если все случаи 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()
{
   ...
}
7
16.09.2008 03:30:13

В .NET Framework 3.5 есть диапазоны:

Enumerable.Range от MSDN

Вы можете использовать его с «Содержит» и оператором 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 такой же, но вы создали небольшой вспомогательный метод (немного многословно, хотя).

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

Вот пример того, как новые функциональные импортированные операторы ИМХО более мощные и элегантные, чем старые императивные.

19
30.10.2008 00:37:06
Хороший выбор. Однако следует отметить одну вещь - Enumerable.Range имеет аргументы int startи int count. Ваши примеры не будут работать так, как они были написаны. Вы пишете это, как будто второй аргумент int end. Например - Enumerable.Range(11,20)приведет к 20 числам, начинающимся с 11, а не к числам от 11 до 20.
Gabriel McAdams 2.08.2012 22:46:06
хотя, если работаешь с Enum, то почему не так? if (Enumerable.Range (MyEnum.A, MyEnum.M) {DoThing ();} else if (Enumerable.Range (MyEnum.N, MyEnum.Z) {DoAnotherThing ();}
David Hollowell - MSFT 6.03.2014 14:22:25
Обратите внимание , что Enumerable.Range(11,20).Contains(c)эквивалентно for(int i = 11; i < 21; ++i){ if (i == c) return true; } return false;Если у вас есть большой выбор , что это займет много времени, а только с помощью >и <будет быстрыми и постоянная время.
Jon Hanna 26.02.2016 14:20:45
Улучшение: MySwitchWithEnumerableвозвращение void- слабый дизайн для этой ситуации. ПРИЧИНА: Вы преобразовали if-elsea в серию независимых операторов - которые скрывают намерение, а именно то, что они являются взаимоисключающими, action- выполняется только одно . Вместо того, чтобы вернуться bool, с телом if (..) { action(); return true; } else return false;Вызывающий сайт затем показывает намерение: if (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));. Это предпочтительнее. Тем не менее, это также больше не является значительным улучшением по сравнению с вашей первоначальной версией для этого простого случая.
ToolmakerSteve 17.01.2017 23:24:05

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

switch (value)
{
    case 1: case 2: case 3:          
        // Do Something
        break;
    case 4: case 5: case 6: 
        // Do Something
        break;
    default:
        // Do Something
        break;
}
695
17.02.2020 09:35:34
'переключатель' должен быть в нижнем регистре для C #?
Austin Harris 18.11.2013 08:08:56
Свернутый код удлиняется до первого примера в вопросе. Можно просто сделать так, как в вопросе.
MetalPhoenix 5.12.2014 19:54:15
Зачем беспокоиться? Автоматический отступ в Visual Studio 2013 все равно вернет его в формат исходного вопроса.
Gustav 29.06.2015 16:09:27
@T_D получает поддержку, потому что на самом деле отвечает на вопрос. ОП сказал, что я что-то упустил ... Карлос ответил тем, чего ему не хватало. Кажется, довольно порезанный и высушенный для меня. Не ненавидь, что у него 422 голоса.
Mike Devenney 30.03.2016 19:42:36
@MikeDevenney Тогда вы по-разному истолковали вопрос, насколько я вижу правильный ответ: «Нет, у c # нет синтаксиса для этого». Если кто-то спросит: «Можно ли налить жидкость в стакан, который я держу вверх ногами?» ответ должен быть «нет», а не «вы можете наливать жидкость вверх, если вы смотрите на нее с ног на голову и используете свое воображение», потому что этот ответ все об использовании воображения. Если вы используете обычный синтаксис, но плохо его форматируете, он выглядит как другой синтаксис с некоторым воображением. Надеюсь, вы
T_D 31.03.2016 13:15:28

Для этого вы должны использовать оператор 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();
    }
-4
9.07.2011 22:41:49
@scone goto нарушает фундаментальный принцип процедурного программирования (из которого все еще укоренены c ++ и c #; это не чистые ОО-языки (слава Богу)). Процедурное программирование имеет четко определенный поток логики, определяемый языковыми конструкциями и соглашениями о вызовах методов (как растет и уменьшается стек времени выполнения). Оператор goto обходит этот поток, позволяя, в основном, произвольно прыгать.
samis 4.12.2012 16:27:28
Я не говорю, что это хороший стиль, конечно, но он делает то, о чем просил оригинальный вопрос.
scone 10.09.2015 20:11:48
Нет, это не «делает то, о чем просил оригинальный вопрос». У исходного вопроса был код, который работал как есть . Им не нужно было это исправить. И даже если они это сделали, это ужасное предложение. Это менее сжато, и использует goto. Хуже того, это совершенно ненужное использование goto, так как оригинальный синтаксис, указанный OP, работает. Вопрос состоял в том, был ли более краткий способ дать альтернативные случаи. Как люди ответили за годы до вас , да, есть - если вы хотите поместить несколько дел в одну строку case 1: case 2:, и если позволяет автоматический стиль редактора.
ToolmakerSteve 17.01.2017 23:29:36
Единственная причина, по которой goto's определяется как плохая, заключается в том, что некоторым людям трудно следовать логике. .Net MSIL (собранный объектный код) использует goto повсеместно, потому что это быстро, но если .Net-код может быть написан и иметь такую ​​же производительность без них, то лучше его не использовать, и поэтому вы не будете огорчены такими людьми, как @ ToolmakerSteve - снисходительный ответ.
dynamiclynk 30.05.2018 21:45:34
@wchoward - Пожалуйста, прочитайте мой ответ более внимательно. Моя жалоба касается не только использования goto . Я возразил, потому что вопрос показал код, который уже работает как есть , и этот ответ а) берет этот рабочий код и делает его более подробным и менее хорошо структурированным, без пользы , б) не отвечает на вопрос.
ToolmakerSteve 31.05.2018 00:00:23

На самом деле мне тоже не нравится команда GOTO, но она есть в официальных материалах Microsoft, и здесь все допустимые синтаксисы.

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

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 можно использовать, и на самом деле это единственный путь к успеху.

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

3
28.02.2020 02:00:02
Обратите внимание, что на практике этого gotoпочти всегда можно избежать (хотя я не считаю его здесь «ужасным» - он выполняет определенную, структурированную роль). В вашем примере, поскольку вы обернули тела case в функции (что хорошо), case 0 может стать CaseZero(); CaseZeroOrOne(); break;. Не gotoтребуется
ToolmakerSteve 17.01.2017 23:45:27
Ссылка наполовину неработающая (перенаправляет «Visual Studio 2003 Удаленная техническая документация» ).
Peter Mortensen 28.02.2020 02:01:18

Кажется, что была проделана огромная работа по поиску способов получить один из наименее используемых синтаксисов 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.

Или вы можете использовать регулярные выражения для проверки символа или использовать функцию 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 для обработки полученного значения перечисления.

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

2
23.02.2012 06:25:00
Вы также можете сопоставить с лямбда-выражением или делегатом
Conrad Frix 26.10.2012 05:56:00
Хорошие моменты. Небольшой комментарий: мне обычно легче вести список значений, соответствующих данному случаю, чем карту массива. Проблема с картой массива в том, что легко ошибиться. Например, вместо карты массивов простых чисел true / falses просто создайте список простых чисел и загрузите их в HashSet для производительности поиска. Даже если существует более двух вариантов, обычно все, кроме одного, представляют собой небольшой список, поэтому создайте либо HashSet перечислений (если они редкие), либо карту массива в коде из списков других случаев.
ToolmakerSteve 17.01.2017 23:39:26

Код ниже не будет работать:

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

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

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

Код, который вы ищете, работает в Visual Basic, где вы легко можете поместить в диапазоны ... в качестве noneопции switchоператора или if elseудобных блоков, я бы посоветовал в очень крайнем случае создать .dll с Visual Basic и импортировать обратно в ваш проект C #.

Примечание: эквивалентный переключатель в Visual Basic есть Select Case.

10
28.02.2020 01:54:13

В C # 7 (доступно по умолчанию в Visual Studio 2017 / .NET Framework 4.6.2) переключение на основе диапазона теперь возможно с помощью оператора switch и поможет решить проблему 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:.
65
28.02.2020 01:53:04
Это (сопоставление с образцом), как правило, должно быть наилучшей практикой, когда вы можете использовать C # 7.x или выше, так как это намного яснее, чем другие ответы.
UndyingJellyfish 30.07.2018 11:42:09
Есть ли способ достичь этого с помощью списка Enums? Где Enums отображаются на int?
Sigex 12.12.2018 11:21:22

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

Так что если у вас есть большой оператор 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
}

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

1
28.02.2020 01:50:24

Вот полное решение 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;
}

Это работает со строками тоже ...

switch (mystring)
{
   case var s when new[] { "Alpha","Beta","Gamma" }.Contains(s):
      // Do something
      break;
...
}
15
28.02.2020 01:48:30
Это будет означать, что вы выделяете массивы для каждого оператора switch, верно? Разве не было бы лучше, если бы они были постоянными переменными?
MaLiN2223 24.12.2019 10:03:55
Элегантно, но было бы неплохо знать, оптимизирует ли этот сценарий компилятор, чтобы повторные вызовы не каждый раз накладывали на конструкцию массива; определение массивов заблаговременно является опцией, но отнимает большую часть элегантности.
mklement0 6.02.2020 18:18:31

Чтобы добавить к разговору, используя .NET 4.6.2, я также смог сделать следующее. Я проверил код, и он работал на меня.

Вы также можете сделать несколько операторов «ИЛИ», как показано ниже:

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

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

            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;
            }
1
3.07.2019 00:32:08
Разве это не зависит от версии C #, а не от версии .NET?
Peter Mortensen 28.02.2020 01:42:06

В C # 7 у нас теперь есть Pattern Matching, поэтому вы можете сделать что-то вроде:

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;
}
4
5.09.2019 09:02:21