【问题标题】:Continue in nested while loops在嵌套的 while 循环中继续
【发布时间】:2010-11-11 03:06:29
【问题描述】:

在这个代码示例中,有没有办法从 catch 块继续外循环?

while
{
   // outer loop

   while
   {
       // inner loop
       try
       {
           throw;
       }
       catch 
       {
           // how do I continue on the outer loop from here?
           continue;
       }
   }
}

【问题讨论】:

  • 嵌套循环只会导致绝望。

标签: c# loops while-loop continue


【解决方案1】:

更新:这个问题是my article on this subject. 的灵感来源,感谢您提出的好问题!


“继续”和“中断”只不过是“goto”的一种令人愉快的语法。显然,通过给它们取可爱的名字并将它们的使用限制在特定的控制结构中,它们不再引起“所有的 goto 都是坏的”人群的愤怒。

如果你想做的是一个 continue-to-outer,你可以简单地在外部循环的顶部定义一个标签,然后“转到”那个标签。如果您觉得这样做不会妨碍代码的可理解性,那么这可能是最方便的解决方案。

但是,我会借此机会考虑您的控制流是否会从重构中受益。每当我在嵌套循环中有条件“中断”和“继续”时,我都会考虑重构。

考虑:

successfulCandidate = null;
foreach(var candidate in candidates)
{
  foreach(var criterion in criteria)
  {
    if (!candidate.Meets(criterion))
    {  // TODO: no point in continuing checking criteria.
       // TODO: Somehow "continue" outer loop to check next candidate
    }
  }
  successfulCandidate = candidate;
  break;
}
if (successfulCandidate != null) // do something

两种重构技术:

首先,将内部循环提取到一个方法中:

foreach(var candidate in candidates)
{
  if (MeetsCriteria(candidate, criteria))
  { 
      successfulCandidate = candidate;
      break;
  }
}

第二,可以所有消除循环吗?如果您因为试图搜索某些内容而循环,则将其重构为查询。

var results = from candidate in candidates 
              where criteria.All(criterion=>candidate.Meets(criterion))
              select candidate;
var successfulCandidate = results.FirstOrDefault();
if (successfulCandidate != null)
{
  do something with the candidate
}

如果没有循环,则无需中断或继续!

【讨论】:

  • +1 表示“...将内部循环提取到方法中。”当我看到嵌套循环时,我需要大量的代码审查理由。它们通常会损害可读性、可维护性和稳定性。 OP 的问题可以通过简单的“返回”或“抛出”来解决(因此不以任何方式依赖 goto)。
  • 当然。当您认为您需要goto 时,请先停下来思考一下您是否真的需要。如果您仍然需要goto,那么就使用它——它在语言中是有原因的。它本质上也不是邪恶的——它只是通常以邪恶的模式出现,因此应该作为停止并尝试发现这种模式的信号(而不是陷入“OMG goto这都是错误的”恐慌)。跨度>
  • Goto 本身并不是邪恶的,但它是通向糟糕、懒惰代码的药物。在所有控制流量的方法中,它通常是最糟糕的。
  • 不要忘记为第二种重构技术添加using System.Linq
  • 那么冗长,哪里像 Java 一样继续 {nameOfLoop} .. :-(
【解决方案2】:
    while
    {
       // outer loop

       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               goto REPEAT;
           }
       }
       // end of outer loop
REPEAT: 
       // some statement or ; 
    }

问题解决了。 (什么??为什么你们都用那种肮脏的眼神看着我?)

【讨论】:

  • 我知道你在那里做了什么
  • 可能无法编译,除非您明确添加空语句分号 (REPEAT: ;)
【解决方案3】:
using System;

namespace Examples
{

    public class Continue : Exception { }
    public class Break : Exception { }

    public class NestedLoop
    {
        static public void ContinueOnParentLoopLevel()
        {
            while(true)
            try {
               // outer loop

               while(true)
               {
                   // inner loop

                   try
                   {
                       throw new Exception("Bali mu mamata");
                   }
                   catch (Exception)
                   {
                       // how do I continue on the outer loop from here?

                       throw new Continue();
                   }
               }
            } catch (Continue) {
                   continue;
            }
        } 
    }

}

}

【讨论】:

    【解决方案4】:

    使用自己的异常类型,例如 MyException。那么:

    while
    {
       try {
       // outer loop
       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               throw MyException;
           }
       }
       } catch(MyException)
       { ; }
    }
    

    这将适用于继续和打破几个级别的嵌套 while 语句。 抱歉格式错误;)

    【讨论】:

    • 你伤害了我的感情,使用异常仅用于流控制。不投反对票,只会伤害感情。 :(
    • 这让我想到处呕吐
    • 这比“伤害我的感情”更强调一点。
    【解决方案5】:

    我认为最好的方法是使用 break 语句。 Break 结束当前循环从结束处继续执行。在这种情况下,它将结束内部循环跳回外部while循环。这就是您的代码的样子:

    while
    {
       // outer loop
    
       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // break jumps to outer loop, ends inner loop immediately.
               break; //THIS IS THE BREAK
           }
       }
    }
    

    我相信这就是您想要实现的目标,对吗? 谢谢!

    【讨论】:

    【解决方案6】:

    你只想打破会延续外部的内部。

    while
    {
       // outer loop
    
       while
       {
           // inner loop
           try
           {
               throw;
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               break;
           }
       }
    }
    

    【讨论】:

    【解决方案7】:

    在内循环中使用break

    【讨论】:

    • 内循环后外循环中有一些代码时不起作用。
    【解决方案8】:

    你可以使用休息;声明。

    while
    {
       while
       {
           try
           {
               throw;
           }
           catch 
           {
               break;
           }
       }
    }
    

    Continue 用于跳回到当前循环的顶部。

    如果您需要突破更多关卡,则必须添加某种“if”或使用可怕/不推荐的“goto”。

    【讨论】:

    • 这个方法的问题是如果在内循环结束和外循环结束之间有额外的工作需要做,调用break时会做,但是调用continue 时不会完成。如果您需要不执行该代码,则需要一个标志。我并不是说这个答案是错误的(哎呀,我赞成),我说它看似简单。
    【解决方案9】:

    没有。
    我建议,将内部循环提取到一个单独的方法中。

    while
    {
       // outer loop
           try
           {
               myMethodWithWhileLoopThatThrowsException()
           }
           catch 
           {
               // how do I continue on the outer loop from here?
               continue;
           }
       }
    }
    

    【讨论】:

    • 这是有问题的,因为单独的方法将无法访问现有的局部变量。
    • 这就是微软给我们函数参数的原因。
    • 将变量作为参数传递,或者如果需要副作用,则将其作为匿名委托发送以在方法中执行。然后编译器将创建一个闭包,保留你的本地范围。
    • 您也不应该将异常处理过程用于正常的代码控制流
    • 这就是为什么微软为我们提供了不需要任何参数的“本地函数”(从 C# 7 和 Visual Studio 2017 开始),因为它们可以访问外部函数的所有局部变量。
    【解决方案10】:

    用内部while循环交换try/catch结构:

    while {
      try {
        while {
          throw;
        }
      }
      catch {
        continue;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-04-16
      • 1970-01-01
      • 2015-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多