【问题标题】:Multiple cases in switch statementswitch 语句中的多个情况
【发布时间】:2010-09-09 06:35:56
【问题描述】:

有没有办法通过多个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 语句(如果您正在检查整数范围)?
  • 是的,charlse,第一种方法很好用,我在很多地方都用过。它比我想要的更脏,但它很有用。我只是以这些整数为例。真实数据更加多样化。 if (1 || 2 || 3 ) {...} else if (4 || 5 || 6) {...} 也可以,但更难阅读。
  • 为什么你认为后者比前者更脏。后者为, 添加了另一种含义,并且不与任何其他c 风格语言共享。这对我来说似乎更脏。
  • 您可能已经从 Ruby 中学习了 2nd 的语法。这就是它在该语言中的工作方式(尽管 switch 变成了 case,case 变成了 when 等等。)
  • 重要提示。从 C# v7 开始的 switch case 支持范围 - 请参阅 Steve G. 的 answer

标签: c# switch-statement


【解决方案1】:

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

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 它得到了支持,因为它实际上回答了这个问题。 OP 说,我是否遗漏了什么……卡洛斯回答说他遗漏了什么。对我来说似乎很切切和干燥。不要讨厌他有 422 票。
  • @MikeDevenney 那么你对这个问题的解释不同,据我所知,正确的答案是“不,c# 没有任何语法”。如果有人问“是否可以将液体倒入我倒置的玻璃杯中?”答案应该是“不”,而不是“如果你倒过来看液体并发挥你的想象力,你可以倒出液体”,因为这个答案就是使用想象力。如果您使用常规语法但格式很糟糕,它看起来像其他语法,有一些想象力。希望你明白我的意思......:P
【解决方案2】:

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

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

【讨论】:

【解决方案3】:

C# 7 的原始答案

C# 7(Visual Studio 2017/.NET Framework 4.6.2 默认提供)中,现在可以使用 switch statement 进行基于范围的切换,这将有助于解决 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 &gt;= 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);

【讨论】:

  • 当您可以使用 C# 7.x 或更高版本时,这(模式匹配)通常应该是最佳实践,因为它比其他答案更清晰。
  • 有没有办法通过枚举列表来实现这一点?枚举映射到 int 的位置?
【解决方案4】:

此语法来自 Visual Basic Select...Case Statement

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# 中使用此语法。相反,您必须使用第一个示例中的语法。

【讨论】:

  • 这是一个怎样的答案? "你不能使用下面的代码"
【解决方案5】:

你可以省略给你的换行符:

case 1: case 2: case 3:
   break;

但我认为这种风格很糟糕。

【讨论】:

  • 糟糕的风格是主观的。我更喜欢这个,因为它清楚地表明了意图。
【解决方案6】:

.NET Framework 3.5 有范围:

Enumerable.Range from 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&lt;ParameterType&gt;。如果需要返回值,请使用Func&lt;ParameterType, ReturnType&gt;

在 C# 3.0 中,Partial Application 不容易封装 case 参数相同的事实,但是您创建了一个小辅助方法(虽然有点冗长)。

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

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

【讨论】:

  • 不错的选择。不过要注意一件事 - Enumerable.Range 有参数 int startint count。您的示例不会按照它们的编写方式正常工作。你把它写成第二个参数是int end。例如 - Enumerable.Range(11,20) 将产生 20 个以 11 开头的数字,而不是从 11 到 20 的数字。
  • 虽然,如果使用枚举,为什么不使用类似的东西? 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 &lt; 21; ++i){ if (i == c) return true; } return false; 如果范围很大,则需要很长时间,而仅使用 &gt;&lt; 会快速且恒定时间。
  • 一项改进:让MySwitchWithEnumerable 返回void 是针对这种情况的弱设计。原因:您已将 if-else 转换为一系列独立语句 - 隐藏了意图,即它们是互斥的 - 只有一个 action 被执行。而是返回bool,正文为if (..) { action(); return true; } else return false;,然后调用站点显示意图:if (MySwitchWithEnumerable(..)) else (MySwitchWithEnumerable(..));。这是优选的。但是,对于这个简单的案例,它也不再是对原始版本的重大改进。
【解决方案7】:

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

In deep tutorial of Relational Patter in C#9

Pattern-matching changes for C# 9.0

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

【讨论】:

  • 啊哈!正确的,当前的答案。现在我们只需等待几年/几十年,它就会慢慢上升......
【解决方案8】:

这是完整的 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 语句分配数组,对吗?如果我们将它们作为常量变量不是更好吗?
  • 优雅,但如果知道编译器是否优化了这种情况确实很好,这样重复调用就不会每次都产生数组构造的开销;提前定义数组是一种选择,但会失去很多优雅。
【解决方案9】:

下面的代码不会工作:

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

这样做的唯一方法是:

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

您正在寻找的代码在 Visual Basic 中工作,您可以在其中轻松放入范围...极端点,使用 Visual Basic 制作 .dll 并导入回您的 C# 项目。

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

【讨论】:

    【解决方案10】:

    另一种选择是使用例程。如果案例 1-3 都执行相同的逻辑,则将该逻辑包装在一个例程中并为每个案例调用它。我知道这实际上并没有摆脱案例陈述,但它确实实现了良好的风格并将维护降至最低.....

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

    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()
    {
       ...
    }
    

    【讨论】:

      【解决方案11】:

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

      【讨论】:

        【解决方案12】:

        switch 在 C# 中的一个鲜为人知的方面是它依赖于 operator=,并且由于它可以被覆盖,因此您可以使用以下内容:

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

        【讨论】:

        • 这可能会成为以后试图阅读代码的其他人的一大难题
        【解决方案13】:

        gcc 实现了对 C 语言的扩展以支持顺序范围:

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

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

        【讨论】:

          【解决方案14】:

          其实我也不喜欢 GOTO 命令,但它在微软官方资料中,这里都是允许的语法。

          如果 switch 部分的语句列表的端点是可到达的,则会发生编译时错误。这被称为“不失败”规则。例子

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

          是有效的,因为没有 switch 部分具有可到达的端点。与 C 和 C++ 不同,switch 部分的执行不允许“落入”下一个 switch 部分,示例

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

          Source

          【讨论】:

          • 请注意,在实践中,goto 几乎总是可以避免的(尽管我不认为它在这里“可怕” - 它正在扮演一个特定的、结构化的角色)。在您的示例中,因为您已将案例主体包装在函数中(一件好事),案例 0 可以变为 CaseZero(); CaseZeroOrOne(); break;。不需要goto
          【解决方案15】:

          在 C# 8.0 中,您可以使用最适合您的情况的新 switch expression 语法。

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

          【讨论】:

            【解决方案16】:

            如果你有大量的字符串(或任何其他类型)都在做同样的事情,我建议使用字符串列表和 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
            }
            

            这适用于任意数量的字符串情况。

            【讨论】:

              【解决方案17】:

              我认为这个在 C# 7 或更高版本中更好。

              switch (value)
              {
                  case var s when new[] { 1,2 }.Contains(s):
                  // Do something
                   break;
              
                  default:
                  // Do the default
                  break;
               }
              

              您还可以在 C# switch case 中检查 Range:Switch case: can I use a range instead of a one number 或者,如果您想了解 C# switch case

              【讨论】:

                【解决方案18】:

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

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

                【讨论】:

                  【解决方案19】:

                  似乎已经投入了大量工作来寻找使 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 {
                          }
                      }
                  

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

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

                  【讨论】:

                  • 您也可以映射到 lambda 表达式或委托
                  • 好点。一个小评论:我通常发现维护与给定情况匹配的值列表比维护数组映射更容易。数组映射的问题是很容易出错。例如,不是真/假的素数数组映射,而是简单地有一个素数列表,并将它们加载到 HashSet 中以提高查找性能。即使有两种以上的情况,通常除了一种情况外都是一个小列表,因此请在代码中从其他情况的列表中构建枚举的 HashSet(如果稀疏)或数组映射。
                  【解决方案20】:

                  只是为了添加到对话中,使用 .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#版本,而不是.NET版本吗?
                  【解决方案21】:

                  一种更漂亮的处理方式

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

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

                  【讨论】:

                    【解决方案22】:

                    我们也可以使用这种方法来实现 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;
                                    }
                            }
                            
                        }
                    

                    【讨论】:

                      【解决方案23】:

                      为此,您将使用 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# 仍然植根于其中;它们不是纯 OO 语言(感谢上帝))。过程式编程具有由语言结构和方法调用约定(运行时堆栈如何增长和缩小)确定的明确定义的逻辑流。 goto 语句基本上通过允许任意跳转来绕过这个流程。
                      • 我并不是说它的风格很好,persay,但它可以满足原始问题的要求。
                      • 不,它没有“做原始问题所要求的”。原始问题的代码按原样运行。他们不需要修复它。即使他们这样做了,这也是一个可怕的建议。它less简洁,使用goto。更糟糕的是,完全没有必要使用 goto,因为 OP 声明的原始语法确实有效。问题是是否有一种更简洁的方式来给出替代案例。正如人们比您早回答的那样,是的,如果您愿意将几个案例放在一行case 1: case 2:,并且如果编辑器的自动样式允许的话。
                      • goto 被确定为坏的唯一原因是因为有些人发现很难遵循逻辑流程。 .Net MSIL(汇编目标代码)全部使用 goto,因为它速度很快,但是如果可以编写 .Net 代码并且没有它们也能保持同样的性能,那么最好不要使用它们,这样你就不会被像 @ 这样的人激怒ToolmakerSteve 居高临下的回复。
                      • @wchoward - 请仔细阅读我的回复。我的抱怨不仅仅是关于 goto 的使用。我反对是因为问题显示了已经按原样工作的代码,而这个答案 a) 采用了该工作代码并使其更冗长,结构更不完善,没有任何好处 b>, b) 没有回答问题。
                      猜你喜欢
                      • 2020-04-16
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多