关于C#:switch语句中有多个case

Multiple cases in switch statement

有没有一种方法可以在不重复说明case value:的情况下通过多个案例陈述?

我知道这是可行的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

但我想这样做:

1
2
3
4
5
6
7
8
9
10
11
12
switch (value)
{
   case 1,2,3:
      //Do Something
      break;
   case 4,5,6:
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

这是我从另一种语言想到的语法,还是我遗漏了什么?


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

1
2
3
4
5
6
7
8
9
10
11
12
switch (value)
{
case 1: case 2: case 3:          
    // Do Something
    break;
case 4: case 5: case 6:
    // Do Something
    break;
default:
    // Do Something
    break;
}


对于你提到的第二种方法,C++中没有语法,也没有C语言。

你的第一种方法没什么问题。但是,如果范围很大,只需使用一系列if语句。


此语法来自Visual Basic Select…Case语句:

1
2
3
4
5
6
7
8
9
10
11
12
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中,使用switch语句现在可以进行基于范围的切换,这将有助于解决OP的问题。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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:


您可以省去换行符,这样可以:

1
2
case 1: case 2: case 3:
   break;

但我认为那是不好的风格。


.NET Framework 3.5具有以下范围:

从msdn开始的可枚举范围

您可以将它与"contains"和if语句一起使用,因为就像有人说的那样,switch语句使用"=="运算符。

这里有一个例子:

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

但我认为我们可以有更多的乐趣:因为您不需要返回值,而且这个动作不带参数,所以您可以很容易地使用动作!

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

这个新方法的旧示例:

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

因为您传递的是动作而不是值,所以应该省略括号,这非常重要。如果需要带参数的函数,只需将Action的类型更改为Action。如果需要返回值,请使用Func

在C 3.0中,没有容易的部分应用程序来封装case参数相同的事实,但是您创建了一个小的helper方法(有点冗长,tho)。

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

下面是一个新的函数导入语句如何比旧的命令式语句更强大和优雅的例子。


@詹妮弗·欧文斯:你说得对,下面的代码是行不通的:

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

唯一的方法是:

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

您正在寻找的代码在VisualBasic上工作,您可以轻松地将范围…在没有选择开关或如果其他块方便,我建议,在非常极端的点,使.dll与VisualBasic和导入回您的C项目。

注意:VisualBasic中的等效开关是Select案例。


另一种选择是使用一个例程。如果案例1-3都执行相同的逻辑,那么将该逻辑包装在一个例程中,并为每个案例调用它。我知道这实际上并不能摆脱case语句,但它确实实现了良好的风格并将维护保持在最低限度。

[编辑]添加了替代实现以匹配原始问题…[/edit]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
switch (x)
{
   case 1:
      DoSomething();
      break;
   case 2:
      DoSomething();
      break;
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}

中高音

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch (x)
{
   case 1:
   case 2:
   case 3:
      DoSomething();
      break;
   ...
}

private void DoSomething()
{
   ...
}

GCC实现了对C语言的扩展,以支持连续范围:

1
2
3
4
5
6
7
8
9
10
11
12
switch (value)
{
   case 1...3:
      //Do Something
      break;
   case 4...6:
      //Do Something
      break;
   default:
      //Do the Default
      break;
}

编辑:刚注意到问题的C标签,所以大概GCC的答案没有帮助。


C中开关的另一个鲜为人知的方面是它依赖于operator=并且由于它可以被覆盖,所以您可以有如下内容:

1
2
3
4
5
6
string s = foo();

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


这是完整的C 7解决方案…

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

也适用于字符串…

1
2
3
4
5
6
7
switch (mystring)
{
   case var s when new[] {"Alpha","Beta","Gamma" }.Contains(s):
      //Do Something
      break;
...
}


实际上,我也不喜欢goto命令,但它在官方的MS材料中,这里都允许使用语法。

如果可以到达switch节的语句列表的端点,则会发生编译时错误。这就是所谓的"不摔倒"规则。实例

1
2
3
4
5
6
7
8
9
10
11
switch (i) {
case 0:
   CaseZero();
   break;
case 1:
   CaseOne();
   break;
default:
   CaseOthers();
   break;
}

有效,因为没有开关节具有可到达的终结点。与C和C++不同,开关部分的执行不允许"下一步"切换到下一个开关部分,并举例说明

1
2
3
4
5
6
7
8
switch (i) {
case 0:
   CaseZero();
case 1:
   CaseZeroOrOne();
default:
   CaseAny();
}

导致编译时错误。当一个switch节的执行后面跟着另一个switch节的执行时,必须使用一个显式的goto case或goto default语句:

1
2
3
4
5
6
7
8
9
10
11
switch (i) {
case 0:
   CaseZero();
   goto case 1;
case 1:
   CaseZeroOrOne();
   goto default;
default:
   CaseAny();
   break;
}

开关部分允许有多个标签。实例

1
2
3
4
5
6
7
8
9
10
11
12
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


许多工作似乎都花在了寻找一种方法,使C最不常用的语法中的一个看起来更好或工作更好。我个人认为switch语句很少值得使用。我强烈建议您分析正在测试的数据以及您想要的最终结果。

例如,我们想快速测试已知范围内的值,看看它们是否是素数。你想避免让你的代码进行浪费性的计算,你可以在网上找到你想要的范围内的素数列表。您可以使用大量的switch语句将每个值与已知素数进行比较。

或者你可以创建素数的数组图,并立即得到结果:

1
2
3
4
5
6
7
8
9
10
11
    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函数来搜索已知十六进制字母字符串中的字符:

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

假设您希望根据1到24之间的值执行三种不同操作中的一种。我建议使用一组if语句。如果这变得太复杂(或者数字更大,比如5个不同的动作,取决于1到90之间的值),那么使用枚举来定义动作并创建枚举的数组映射。然后,该值将用于索引到数组映射中,并获取所需操作的枚举。然后使用一小组if语句或一个非常简单的switch语句来处理产生的枚举值。

此外,数组映射将一系列值转换为动作的好处在于它可以很容易地被代码更改。使用硬接线代码,您不可能在运行时轻松更改行为,但使用数组映射,这很容易。


如果您有大量的字符串(或任何其他类型)大小写都在执行相同的操作,我建议使用与string.contains属性组合的字符串列表。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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语句替换它:

1
2
3
4
5
6
7
8
9
10
11
//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语句。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    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();
    }