Есть ли способ пропустить несколько 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;
}
Это синтаксис, о котором я думаю, на другом языке, или я что-то упускаю?
да, чарльз, первый способ работает нормально, я использовал его во многих местах. Это грязнее, чем хотелось бы, но полезно. Я просто использовал эти целые числа в качестве примера. Реальные данные были более разнообразными. If (1 || 2 || 3) {...} else if (4 || 5 || 6) {...} тоже сработало бы, но его труднее читать.
почему вы считаете второе более грязным, чем первое. Последний придает , еще одно значение, которое не используется ни в одном другом языке c-стиля. Мне это показалось бы намного грязнее.
Вы могли позаимствовать синтаксис второго из Ruby. Вот как это работает на этом языке (хотя, среди прочего, switch становится case, а case - when).
VB имеет гораздо больше возможностей в этой области со своим заявлением select case.
Вы, вероятно, думаете о Delphi / Pascal, который поддерживает синтаксис типа case i when 1...3: begin end; 4, 5, 7: begin end; 6, 8..10: begin end; else // handle default end;.
Главный пример здесь на самом деле намного короче и его легче читать, чем все ответы «вот как вы бы сделали это сегодня в причудливом или перечисляемом диапазоне», которые не подходят для моего сверхширокого экрана. Я имею в виду. Вы смотрите на него, и становится понятно, что он делает.





Для второго упомянутого вами метода нет синтаксиса ни в C++, ни в C#.
В вашем первом методе нет ничего плохого. Однако, если у вас очень большие диапазоны, просто используйте серию операторов if.
В качестве дополнения я хотел добавить ссылку на спецификацию языка C#, доступную в MSDN по адресу msdn.microsoft.com/en-us/vcsharp/aa336809.aspx
Пользователь может использовать некоторые if (или поиск в таблице), чтобы уменьшить ввод до набора перечислений и включить перечисление.
Я считаю, что это уже неверно. См. stackoverflow.com/questions/20147879/…. Также на этот самый вопрос есть ответ stackoverflow.com/a/44848705/1073157
Этот синтаксис взят из Visual Basic Выберите ... Заявление о регистре:
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# заключается в том, что он полагается на оператор =, и, поскольку он может быть переопределен, у вас может быть что-то вроде этого:
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 не помогает.
Вы можете опустить новую строку, которая дает вам:
case 1: case 2: case 3:
break;
но я считаю это плохим стилем.
Плохой стиль субъективен. Я предпочитаю это, так как это ясно показывает намерение.
Другой вариант - использовать рутину. Если все случаи 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()
{
...
}
.NET Framework 3.5 имеет диапазоны:
вы можете использовать его с выражением «содержит» и оператором 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);
}
Вот пример того, как новый функциональный импортированный оператор IMHO более мощный и элегантный, чем старый императивный.
Хороший выбор. Однако следует отметить одну вещь - Enumerable.Range имеет аргументы int start и int count. Ваши примеры не будут работать так, как они были написаны. Вы пишете это так, как будто второй аргумент - int end. Например, Enumerable.Range(11,20) приведет к 20 числам, начинающимся с 11, а не к числам от 11 до 20.
хотя, если работать с Enum, почему бы не что-то вроде? if (Enumerable.Range (MyEnum.A, MyEnum.M) {DoThing ();} else if (Enumerable.Range (MyEnum.N, MyEnum.Z) {DoAnotherThing ();}
Обратите внимание, что Enumerable.Range(11,20).Contains(c) эквивалентен for(int i = 11; i < 21; ++i){ if (i == c) return true; } return false;. Если бы у вас был большой диапазон, это заняло бы много времени, в то время как использование > и < было бы быстрым и постоянным.
Улучшение: возврат MySwitchWithEnumerablevoid - слабый дизайн для этой ситуации. ПРИЧИНА: вы преобразовали if-else в серию независимых операторов - это скрывает намерение, которое состоит в том, что они исключают друг друга - выполняется только один action. Вместо этого верните bool с телом if (..) { action(); return true; } else return false;. Затем вызывающий сайт показывает намерение: if (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));. Это предпочтительнее. Однако в этом простом случае это также не является значительным улучшением по сравнению с исходной версией.
Думаю, на это уже был дан ответ. Однако я думаю, что вы все равно можете смешать оба варианта синтаксически лучше, выполнив:
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 Studio 2013 все равно вернет это к формату исходного вопроса.
@JeremyChild Может быть, потому что этот ответ - всего лишь замаскированная копия вопроса. Один из редких моментов, когда я отклоняю ответ. На самом деле, как это получило такую поддержку?
@T_D он получает поддержку, потому что действительно отвечает на вопрос. Оператор сказал, я что-то упустил ... Карлос ответил, что ему не хватало. Мне это кажется довольно скучным. Не жалейте, что у него 422 голоса за.
@MikeDevenney Тогда вы интерпретировали вопрос по-другому, насколько я понимаю, правильным ответом будет «нет, в C# нет синтаксиса для этого». Если кто-то спросит "можно ли налить жидкость в стакан, который я держу вверх ногами?" ответ должен быть «нет», а не «вы можете вылить жидкость, если вы посмотрите на нее вверх ногами и задействуете свое воображение», потому что этот ответ основан на использовании воображения. Если вы используете обычный синтаксис, но плохо его форматируете, он выглядит как другой синтаксис, но с некоторым воображением. Надеюсь, вы поняли мою мысль ...: P
@MikeDevenney Например, я не проголосовал за этот ответ, потому что это тот же ответ, который Аллан Винд дал за два года до этого ответа! с небольшим количеством ненужного добавленного вздора .... ох, и я поддержал ответ Аллана;)
Это также приведет к появлению длинных строк, если вы используете перечисления, что в основном имеет место для операторов switch case ....
Мне это нравится, потому что он лучше отражает происходящее, чем складывание пустых ящиков над тем, что делает что-то. Т.е. Любой из них, делать что-нибудь; скорее, чем, возможно, была создана куча кейсов, и программист пропустил что-то в одном или нескольких из них. Это лучше показывает намерение.
Для этого вы должны использовать инструкцию 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();
}
@scone goto нарушает фундаментальные принципы процедурного программирования (из которых C++ и C# все еще имеют корни; это не чистые языки объектно-ориентированного программирования (слава богу)). Процедурное программирование имеет четко определенный поток логики, определяемый языковыми конструкциями и соглашениями о вызове методов (как размер стека времени выполнения увеличивается и уменьшается). Оператор goto обходит этот поток, в основном разрешая произвольные прыжки.
Я не говорю, что это хороший стиль, но он выполняет то, о чем просил исходный вопрос.
Нет, он не «выполняет то, о чем просил исходный вопрос». Исходный вопрос имел код это работало как есть. Им не нужно было это исправлять. И даже если они это сделали, это ужасное предложение. Его меньше краток и использует goto. Хуже того, это совершенно ненужное использование goto, поскольку исходный синтаксис, заявленный OP, действительно работает. Вопрос заключался в том, существует ли способ более лаконичный указать альтернативные случаи. Как люди ответили лет до того, как ты это сделал, да, есть - если вы хотите поместить несколько случаев в одну строку case 1: case 2:, и если позволяет автоматический стиль редактора.
Единственная причина, по которой goto считаются плохими, заключается в том, что некоторым людям трудно следовать логическому потоку. .Net MSIL (собранный объектный код) использует goto повсюду, потому что он быстрый, но если .Net-код может быть написан и будет столь же производительным без них, лучше не использовать их, чтобы вас не обидели такие люди как @ Снисходительный ответ Стива инструментальщика.
@wchoward - Пожалуйста, прочтите мой ответ внимательнее. Моя жалоба касается не только использования идти к. Я возразил, потому что вопрос показал код, который уже работает как есть, и этот ответ a) берет этот рабочий код и делает его более подробным и менее структурированным, без пользы, b) не отвечает на вопрос.
На самом деле мне тоже не нравится команда GOTO, но она есть в официальных материалах Microsoft, и здесь все разрешенные синтаксисы.
Если конечная точка списка операторов раздела переключателя достижима, возникает ошибка времени компиляции. Это известно как правило «без провалов». Пример
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 можно использовать, и на самом деле это единственный путь к провалу.
Обратите внимание, что на практике goto почти всегда можно избежать (хотя я не считаю его здесь «ужасным» - он выполняет определенную, структурированную роль). В вашем примере, поскольку вы обернули тела case в функции (хорошо), case 0 может стать CaseZero(); CaseZeroOrOne(); break;. goto не требуется.
Ужасно много работы, похоже, было потрачено на поиск способов заставить один из наименее используемых синтаксисов 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 для обработки полученного значения перечисления.
Кроме того, хорошая вещь в карте массива, которая преобразует диапазон значений в действия, заключается в том, что ее можно легко изменить с помощью кода. С жестко привязанным кодом вы не можете легко изменить поведение во время выполнения, но с картой массива это легко.
Вы также можете сопоставить лямбда-выражение или делегат
Хорошие моменты. Один небольшой комментарий: мне обычно легче поддерживать список значений, соответствующих данному случаю, чем карту массива. Проблема с картой массива в том, что здесь легко ошибиться. Например, вместо карты массива простых чисел true / false просто имейте список простых чисел и загрузите их в HashSet для выполнения поиска. Даже если имеется более двух случаев, обычно все случаи, кроме одного, представляют собой небольшой список, поэтому создайте либо HashSet перечислений (если они редкие), либо карту массива в коде из списков других случаев.
Код ниже не будет работает:
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.
В C# 7 (доступно по умолчанию в Visual Studio 2017 / .NET Framework 4.6.2) переключение на основе диапазона теперь возможно с помощью оператор переключения и поможет решить проблему 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:.Это (сопоставление с образцом), как правило, должно быть лучшей практикой, когда вы можете использовать C# 7.x или выше, поскольку он намного яснее, чем другие ответы.
Есть ли способ добиться этого с помощью списка перечислений? Где преобразование Enums в int?
Если у вас очень большое количество строк (или кейсов любого другого типа), которые делают одно и то же, я рекомендую использовать список строк в сочетании со свойством 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
}
Это хорошо масштабируется для любого количества строк.
Вот полное решение 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;
...
}
Это означало бы, что вы выделяете массивы для каждого оператора switch, верно? Разве не было бы лучше, если бы они были постоянными переменными?
Элегантно, но действительно было бы хорошо знать, оптимизирует ли компилятор этот сценарий, чтобы повторные вызовы не вызывали каждый раз накладные расходы на построение массива; определение массивов заранее - это вариант, но он сильно отнимает у них элегантность.
Чтобы добавить к разговору, используя .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;
}
Разве это не зависит от версии C#, а не от версии .NET?
В C# 7 теперь есть Соответствие шаблону, поэтому вы можете делать что-то вроде:
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;
}
Я думаю, что это лучше на C# 7 или выше.
switch (value)
{
case var s when new[] { 1,2 }.Contains(s):
// Do something
break;
default:
// Do the default
break;
}
Вы также можете проверить диапазон в случае переключения C#: Случай переключателя: могу ли я использовать диапазон вместо одного числа Или, если вы хотите понять основы C# switch case
В C# 9 появилась функция соответствия реляционным образцам. Это позволяет нам делать:
switch (value)
{
case 1 or 2 or 3:
// Do stuff
break;
case 4 or 5 or 6:
// Do stuff
break;
default:
// Do stuff
break;
}
В подробном руководстве по Relational Patter в C# 9
Изменения сопоставления с образцом для C# 9.0
Relational patterns permit the programmer to express that an input value must satisfy a relational constraint when compared to a constant value
У вас также могут быть совершенно другие условия
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;
}
Есть ли причина, по которой вы просто не используете оператор IF (если вы проверяете диапазон целых чисел)?