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

我想的这个语法是来自不同的语言,还是我遗漏了什么?

1190929 次浏览

你提到的第二种方法在C++和C#中都没有语法。

你的第一个方法没有问题。但是,如果你有非常大的范围,只需使用一系列if语句。

此语法来自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]

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的范围:

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参数相同的事实,但是您创建了一个小助手方法(有点冗长,tho)。

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

这里有一个例子,说明新的函数导入语句如何比旧的命令式语句更强大和优雅。

我想这已经回答了。但是,我认为你仍然可以通过以下操作以更好的语法方式混合这两个选项:

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

为此,您可以使用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();
}

实际上,我也不喜欢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();
}

导致编译时错误。当执行Switch部分之后要执行另一个Switch部分时,必须使用显式goto case或goto default语句:

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,它实际上是失败的唯一方法。

源代码

似乎已经投入了大量的工作来寻找让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函数在已知十六进制字母的字符串中搜索char:

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

假设你想根据1到24范围内的值执行3种不同的操作之一。我建议使用一组IF语句。如果变得太复杂(或者数字更大,例如根据1到90范围内的值执行5种不同的操作),那么使用枚举来定义操作并创建枚举的数组映射。然后该值将用于索引到数组映射中并获得你想要的操作的枚举。然后使用一组小IF语句或一个非常简单的Switch语句来处理生成的枚举值。

此外,将一系列值转换为操作的数组映射的好处在于它可以通过代码轻松更改。使用硬连线代码,您无法轻松更改运行时的行为,但使用数组映射很容易。

下面的代码不会工作:

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

做到这一点的唯一方法是:

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

您正在寻找的代码在Visual Basic中工作,您可以轻松地将范围……在switch语句的none选项或if else块方便,我建议在非常极端的情况下,使用Visual Basic制作. dll并导入回您的C#项目。

注意:Visual Basic中的开关等效项是Select Case

C#7的原始答案

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:

C#9的更新示例

switch(myValue)
{
case <= 0:
Console.WriteLine("Less than or equal to 0");
break;
case > 0 and <= 10:
Console.WriteLine("More than 0 but less than or equal to 10");
break;
default:
Console.WriteLine("More than 10");
break;
}

var message = myValue switch
{
<= 0 => "Less than or equal to 0",
> 0 and <= 10 => "More than 0 but less than or equal to 10",
_ => "More than 10"
};
Console.WriteLine(message);

如果您有大量的字符串(或任何其他类型)case都在做同样的事情,我建议使用与string. Con的属性相结合的字符串列表。

所以如果你有一个像这样的大开关语句:

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

添加到对话中,使用. NET 4.6.2我还可以执行以下操作。 我测试了代码,它确实为我工作。

您还可以执行多个“OR”语句,如下所示:

            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#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#开关案例中检查Range:切换情况:我可以使用范围而不是一个数字吗

 int i = 3;


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

切换情况下多个条件在C#

或者如果你想了解 C#开关盒

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

在C#9的关系模式的深度教程中

C#9.0的模式匹配更改

关系模式允许程序员表达输入 值与常量相比时必须满足关系约束 值

你也可以有完全不同的条件

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

在C#8.0中,您可以使用新的开关表达式语法,这非常适合您的情况。

var someOutput = value switch
{
>= 1 and <= 3 => <Do some stuff>,
>= 4 and <= 6 => <Do some different stuff>,
_ => <Default stuff>
};

一个更美好的方式来处理它

if ([4, 5, 6, 7].indexOf(value) > -1)
//Do something

您可以对具有相同结果的多个值执行此操作

我们还可以使用这种方法来实现Switch语句中的多情况……您可以使用这种方法使用任意数量的条件。

    int i = 209;
int a = 0;
switch (a = (i>=1 && i<=100) ? 1 : a){
case 1:
System.out.println ("The Number is Between 1 to 100 ==> " + i);
break;
default:
switch (a = (i>100 && i<=200) ? 2 : a) {
case 2:
System.out.println("This Number is Between 101 to 200 ==> " + i);
break;
        

default:
switch (a = (i>200 && i<=300) ? 3 : a) {
case 3:
System.out.println("This Number is Between 201 to 300 ==> " + i);
break;
                

default:
// You can make as many conditions as you want;
break;
}
}
        

}

使用新版本的C#我已经这样做了

public string GetValue(string name)
{
return name switch
{
var x when name is "test1" || name is "test2" => "finch",
"test2" => somevalue,
_ => name
};
}