在C#中重新启动foreach循环?
如何在C#中重新启动在C#中重新启动foreach循环?,c#,loops,foreach,reserved-words,C#,Loops,Foreach,Reserved Words,如何在C#中重新启动foreach循环 例如: Action a; foreach(Constrain c in Constrains) { if(!c.Allows(a)) { a.Change(); restart; } } restart这里类似于continue或break,但它从一开始就重新启动foreach 这就像将循环的计数器再次设置为0一样 这在C#中可能吗 编辑:我要感谢Mehrdad Afshari和Mahesh Velaga让我在
foreach
循环
例如:
Action a;
foreach(Constrain c in Constrains)
{
if(!c.Allows(a))
{
a.Change();
restart;
}
}
restart
这里类似于continue
或break
,但它从一开始就重新启动foreach
这就像将循环的计数器再次设置为0一样
这在C#中可能吗
编辑:我要感谢Mehrdad Afshari和Mahesh Velaga让我在当前的实现中发现了一个bug(index=0),否则就不会被发现。使用好的旧转到
:
restart:
foreach(Constrain c in Constrains)
{
if(!c.Allows(a))
{
a.Change();
goto restart;
}
}
如果你100%的时间都是因为某种原因被诊断患有恐惧症(没有原因这不是一件好事),你可以尝试使用一个标志来代替:
bool restart;
do {
restart = false;
foreach(Constrain c in Constrains)
{
if(!c.Allows(a))
{
a.Change();
restart = true;
break;
}
}
} while (restart);
void Main()
{
数不清的犯人;
某物a;
而(!TryChangeList(cons,a)){}
}
//名称tryChangeList显示了列表将被更改的意图
私有bool TryChangeList(IEnumerable约束,SomeObject a)
{
foreach(约束中的var con)
{
如果(!c.允许(a))
{
a、 改变();
返回false;
}
}
返回true;
}
一种方法是使用,正如您已经提到的:
在这里重新启动就像继续或中断一样
但它会重新启动foreach
开始时类似于将for循环的计数器再次设置为0
动作a;
对于(var index=0;index
虽然这是一条非常古老的线索,但没有一个答案对该代码的语义给予了应有的关注:
- 您对
a
- 如果
a
破坏了其中任何一个,请尝试另一个a
并将其推过链条
也就是说,a.Change()
应与约束检查循环分离,同时遵循CQS原则:
while (!MeetsConstraints(a))
{
a.Change();
}
bool MeetsConstraints(Thing a)
{
return Constraints.All(c => c.Allows(a));
}
没有后路,没有丑陋的循环,只是简单和干净 这是错误的index=-1
可以工作,如果可枚举项是索引可访问的,但即使在这种情况下,它也会使代码难以读取,并且使意图不明确。这是一个因盲目恐惧症而使事情变得更糟的完美例子。至少,对于goto
,目的非常清楚。+1这是第一个想到的解决方案——为什么不使用for循环呢?为什么要创建一个bastardized foreach解决方案。@Chuck:可能是因为并非所有集合都可以通过索引访问?@Chuck这种方法还有其他问题。它很容易出错(刚刚用index=0
对答案进行的原始修订证明了这一点)。用指数操纵事物只会带来更多的问题。更不用说操纵循环索引是不受欢迎的了。@Chuck:是的,但我认为你不应该通过操纵循环内的循环索引来重置for
循环。我相信goto
比将索引设置为-1更具可读性,也更不容易出错。了解需要在何处使用这种重新启动可能会很有趣。您正在某种算法中使用可变对象列表。你能分享你试图解决的实际问题吗?实际问题:一些代理试图在一个环境中移动。环境中存在障碍。在每个代理决定下一个动作是什么之后,环境检查代理是否会跨越障碍,如果是,环境允许代理选择另一个动作;在这里,我需要重新启动foreach循环,以便使用新选择的操作再次检查所有屏障。。我希望这能说明…+1。在这种情况下,重构通常是一个很好的解决方案,只要它是可行的,并且不会使事情变得更复杂。@John-如果您将IList更改为IEnumerable,它也会适用于IEnumerable。Lei不是下选者,但您应该将IList
更改为IEnumerable
。我已经解释过了。问题并没有局限于IList
,正确的方法名称应该是checkConstraints和fNotMetChangeaOnce,后面没有checkingConstraints
。或者,您可以删除行a.Change()
并将其放入while
主体中,使该方法成为纯粹的约束检查:while(!meetConstraints(cons,a)){a.Change();}
。重构是个好主意,但要做得恰当。这样,该方法仍然混合了值更改和约束检查逻辑,而值更改现在分布在两个方法中。您需要先处理原始方法。@John:的确如此。你必须处理潜在的例外情况。。。这最终会导致使用
语句生成一个,在这一点上,您会意识到,您首先不应该抛弃foreach
!小心!在dotnet3.5及更高版本(LINQ等)中,foreach()可以拉入闭包。此解决方案或跳出循环的任何其他解决方案将正确处理闭包。但是仔细想想;这是一个精心设计可以节省大量调试的地方。你为什么要这样做?我是说它解决了什么问题?我很高兴看到原作是什么,而不是人为的代码。我喜欢“好老的goto”和“gotophobia”。然而,你对哥特恐惧症患者的建议一点也不好break
是一个带有另一个名称的goto
。它只会跳转到错误的位置,您必须使用一个标志和一个额外的do while循环来修复它——这比解决方案1要难看得多。你可能知道:-)但一旦你想建议一个非后进版本,它应该是真正的非后进版本。或者你可以完全删除那个部分,因为它已经被马赫什的答案覆盖了。@chiccodoro gotophobists倾向于认为break
出于某种原因是可以的。@Wolfzoon它是真的break是一种goto,就像扔和扔一样。不同之处在于,它是一种标准使用的goto,具有定义良好的结构,现代经验丰富的程序员可以使用它
Action a;
for(var index = 0; index < Constratins.Count; index++)
{
if(!Constraints[index].Allows(a))
{
a.Change();
index = -1; // restart
}
}
for (var en = Constrains.GetEnumerator(); en.MoveNext(); )
{
var c = en.Current;
if (!c.Allows(a))
{
a.Change();
en = Constrains.GetEnumerator();
}
}
while (!MeetsConstraints(a))
{
a.Change();
}
bool MeetsConstraints(Thing a)
{
return Constraints.All(c => c.Allows(a));
}