【问题标题】:Break out of a while loop that contains a switch statement跳出包含 switch 语句的 while 循环
【发布时间】:2010-12-31 12:54:01
【问题描述】:

我无法弄清楚如何跳出包含 switch 语句的循环。 Break 是从开关中跳出来的,而不是循环中的。

对此可能有更优雅的解决方案。我已经实现了一个标志,该标志以 true 开头并设置为 false 并结束循环。您能提供更好的解决方案吗?

背景:此代码用于条码工作流系统。我们有内置条形码扫描仪的 PocketPC。此代码用于其中一项功能。它在整个例程中提示用户输入不同的数据。这部分允许他们滚动浏览在 PocketPC 终端上显示该信息的一些库存记录(分页结果),并允许他们输入“D”表示完成,“Q”表示退出。

这是当前需要改进的 C# 示例:

do
{
    switch (MLTWatcherTCPIP.Get().ToUpper())
    {
        case "": //scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "P": //scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
            break;
        case "D": //DONE (exit out of this Do Loop)
            // break; // this breaks out of the switch, not the loop
            // return; // this exists entire method; not what I'm after
            keepOnLooping = false;
            break;
        case "Q": //QUIT (exit out to main menu)
            return;
        default:
            break;
    }
} while (keepOnLooping);

这是在 VB.NET 中执行此操作的代码示例

Do
    Select Case MLTWatcherTCPIP.Get().ToUpper
        Case "" ''#scroll/display next inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown()
        Case "P" ''#scroll/display previous inventory location
            MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp()
        Case "D" ''#DONE (exit out of this Do Loop)
            Exit Do
        Case "Q" ''#QUIT (exit out to main menu)
            Return
    End Select
Loop

谢谢,

【问题讨论】:

  • 这对我来说很好,标志变量是检查循环条件的标准方法。
  • 在 Java(和其他一些)中,标记循环并使用 labelled break 将是最直接的答案。

标签: c# while-loop break


【解决方案1】:

对于多级中断,您必须使用 goto 语句。它似乎是 C# 中唯一的“干净”方式。使用标志也很有用,但如果循环有其他运行困境,则需要额外的代码。

http://msdn.microsoft.com/en-us/library/aa664756(VS.71).aspx

注意到其他一些非 c 语言通过 break <em>levels</em>; 进行多级中断可能会很有趣(不过,Java 也一样没用,因为它使用 goto 伪装成 continue.. :P)

【讨论】:

    【解决方案2】:

    标志是执行此操作的标准方法。我知道的唯一其他方法是使用goto

    【讨论】:

      【解决方案3】:

      我会尽量避免,但你可以使用...

      goto

      但是,如果您选择这样做,拿着干草叉的愤怒暴徒会成为一种职业危害。

      【讨论】:

      • 超过 15 年的经验告诉我,有时 goto 是最好的、快速和优化的选项,将算法保留在一个小地方:保持快速、高效、原子,而不会破坏它一千件,减少不必要的通话。示例,声波分析、机器视觉、实时等。标志不会使其变慢,我认为这是不可能的,但许多标志可能是阅读问题。
      【解决方案4】:

      我知道的唯一其他方法是可怕的 goto。 MSDN also says this.

      但是,我认为您没有理由在这种情况下使用它。您实现的方式运行良好,并且比 goto 更易于维护。我会保留你所拥有的。

      【讨论】:

      • 虽然goto 可能令人恐惧,但在少数情况下它确实有用,我相信这就是其中之一。
      【解决方案5】:

      IMO,这似乎是打破while 循环的完美方法。它可以满足您的期望,而且没有副作用。我可以考虑做

      if(!keepOnLooping)
        break;
      

      但这在执行方面并没有什么不同。

      【讨论】:

        【解决方案6】:

        这里的一个选择是将这个循环重构为一个方法(“提取方法”),并使用return

        【讨论】:

          【解决方案7】:

          将其包装成一个函数并使用 return 语句退出。怎么样?

          【讨论】:

            【解决方案8】:

            您可以将 switch 语句更改为 for/foreach 循环。一旦满足条件,将“keepOnLooping”设置为 false,然后使用 break 退出循环。其余的应该自己处理。

            【讨论】:

            • 切换到for/foreach?你的意思是别的。
            【解决方案9】:

            为什么不将开关包装到一个返回布尔值的方法中以继续循环?它的附带好处是使代码更具可读性。有人写了一篇论文说我们毕竟不需要 goto 语句是有原因的 ;)

            do
            {
                bool keepOnLooping = TryToKeepLooping();
            } while (keepOnLooping);
            
            private bool TryToKeepLooping()
            {
                switch (MLTWatcherTCPIP.Get().ToUpper())
                {
                    case "": //scroll/display next inventory location
                        MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                        break;
                    case "P": //scroll/display previous inventory location
                        MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                        break;
                    case "D": //DONE (exit out of this Do Loop)
                        // break; // this breaks out of the switch, not the loop
                        // return; // this exists entire method; not what I'm after
                        return false;
                    case "Q": //QUIT (exit out to main menu)
                        return true;
                    default:
                        break;
                }
            
                return true;
            }
            

            【讨论】:

              【解决方案10】:

              您可以将switch 语句替换为if/else 语句。不需要goto 并且break 语句离开循环:

              do
              {
                String c = MLTWatcherTCPIP.Get().ToUpper();
              
                if (c = "")
                  MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                else if (c = "P")
                  MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextUp();
                else if (c = "D")
                   break;
                else if (c = "Q")
                  return;
                else
                {
                  // Handle bad input here.
                }
              } while (keepLooping)
              

              【讨论】:

              • 虽然这对于大多数应用程序来说都很好,但switchif/else 之间的一个重要区别是编译器通常可以通过使用跳转表来优化“分支”操作,从而更快代码。
              • Steve,这里的代码正在等待用户输入。它已经在那里坐了数十亿纳秒,完全什么也没做。算出输入的字符是什么,需要 1 纳秒还是 100 纳秒似乎完全无关紧要。优化应该由有关真实世界用户关注的经验数据驱动,而不是由对编译器可能会做什么的随意猜测。
              • 我同意史蒂夫的论点,但在某些简单的情况下,将您的逻辑更改为 if/else 就可以解决问题。
              【解决方案11】:

              我发现这个表格更易读:

              bool done = false;
              while (!done) 
              { 
                  switch (MLTWatcherTCPIP.Get().ToUpper()) 
                  { 
                      case "": //scroll/display next inventory location 
                          MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
                          break; 
                      case "P": //scroll/display previous inventory location 
                          MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown(); 
                          break; 
                      case "D": //DONE (exit out of this Do Loop) 
                          done = true;
                          break; 
                      case "Q": //QUIT (exit out to main menu) 
                          return; 
                      default: 
                          break; 
                  } 
              }
              

              【讨论】:

              • 为什么不if/else
              • @Ozkan 我只是在回答问题。我实际上永远不会以这种方式使用 ToUpper() 进行不区分大小写的比较。
              【解决方案12】:

              你不能轻易跳出外循环,但你可以continue它。

              如果你颠倒你的逻辑,那么你就会明白这一点。 注意在 switch 语句之后有一个break 来退出循环。

              在我看来,这不是很可读的代码,我认为标志仍然是最好的。

                 do
                       {
                          switch (Console.ReadKey().KeyChar.ToString())
                          {
                              case "U":
                                  Console.WriteLine("Scrolling up");
                                  continue;
              
                              case "J":
                                  Console.WriteLine("Scrolling down");
                                  continue;
              
                              case "D": //DONE (exit out of this Do Loop)
                                  break;
              
                              case "Q": //QUIT (exit out to main menu)
                                  return;
              
                              default:
                                  Console.WriteLine("Continuing");
                                  continue;
                          }
              
                          break;
              
                      } while (true);
              
                      Console.WriteLine("Exited");
              

              【讨论】:

                【解决方案13】:

                这样写:

                case "Exit/Break" :
                                  //Task to do
                                    if(true)
                                      break;
                

                此中断不会与任何情况相关联。它将属于while 循环。

                【讨论】:

                  【解决方案14】:

                  另一个(不太好)的替代方法是唯一处理case,您必须立即使用if“跳出循环”并将其移出switch块。如果开关盒很长,那就不是很优雅:

                  do
                  {
                      var expression = MLTWatcherTCPIP.Get().ToUpper();
                      if (expression = "D") //DONE (exit out of this Do Loop)
                      {   
                          statement;
                          break;
                      }
                  
                      switch (expression)
                      {
                          case "": //scroll/display next inventory location
                              MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                              break;
                          case "P": //scroll/display previous inventory location
                              MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                              break;
                          case "Q": //QUIT (exit out to main menu)
                              return;
                          default:
                              break;
                      }
                  } while (true); //or whatever your condition is
                  

                  您也可以将case 本身作为while 循环条件的一部分,因为您只需跳出循环并且表达式本身的计算很简单(如读取变量)。

                  do
                  {
                      switch (expression)
                      {
                          case "": //scroll/display next inventory location
                              MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                              break;
                          case "P": //scroll/display previous inventory location
                              MLTWatcherTCPIP.TerminalPrompt.ScrollBodyTextDown();
                              break;
                          case "Q": //QUIT (exit out to main menu)
                              return;
                          default:
                              break;
                      }
                  } while (condition && expression != "D");
                  

                  此外,如果由于某种原因将整个事物重构为新方法(这是最优雅的解决方案)是不可接受的,那么您也可以依靠匿名委托在现有方法中执行相同操作。

                  【讨论】:

                    【解决方案15】:

                    可能会也可能不会,但是 lamda 为什么不试试看呢

                    while(  (expr) => (){
                    switch(expr){
                    case 1: dosomething; return true; 
                    case 2 : something;return true;
                    case exitloop:return false;}
                    });   
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2013-12-20
                      • 1970-01-01
                      • 2013-10-21
                      • 2016-02-19
                      • 2018-09-16
                      • 2017-06-16
                      • 1970-01-01
                      • 2023-04-10
                      相关资源
                      最近更新 更多