是否可以打破嵌套循环?

时间:2011-10-24 21:53:12

标签: c# javascript

JavaScript支持类似goto的语法来打破嵌套循环。一般来说这不是一个好主意,但它被认为是可以接受的做法。 C#不直接支持break labelName语法...但它确实支持臭名昭着的goto

我相信可以在C#中实现等价物:

    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        for(int j = 0; j < 3; j++)
            if (i > 5)
            {
                goto Break;//break out of all loops
            }
    }

    Break:

通过JavaScript的相同逻辑,嵌套循环场景是goto的可接受用法吗?否则,我知道实现此功能的唯一方法是设置具有适当范围的bool

10 个答案:

答案 0 :(得分:28)

我的观点:嵌套循环的复杂代码流很难推理;分支,无论是goto还是break,都会让它变得更难。我不会写goto,而是先考虑是否有办法消除嵌套循环。

一些有用的技巧:

第一种技术:将内循环重构为方法。让方法返回是否突破外循环。所以:

for(outer blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             goto leaveloop;      
        }
    }
}
leaveloop:    
...

变为

for(outer blah blah blah)
{
    if (Inner(blah blah blah))
        break;
}

...

bool Inner(blah blah blah)
{
    for(inner blah blah blah)
    {
        if (whatever)
        {
             return true;      
        }
    }
    return false;
}

第二种技术:如果循环没有副作用,请使用LINQ。

// fulfill the first unfulfilled order over $100
foreach(var customer in customers)
{
    foreach(var order in customer.Orders)
    {
        if (!order.Filled && order.Total >= 100.00m)
        {
             Fill(order);
             goto leaveloop;      
        }
    }
}
leaveloop:    

相反,写:

var orders = from customer in customers
             from order in customer.Orders;
             where !order.Filled
             where order.Total >= 100.00m
             select order;
var orderToFill = orders.FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

没有循环,所以不需要突破。

或者,正如配置程序在注释中指出的那样,您可以用以下形式编写代码:

var orderToFill = customers
    .SelectMany(customer=>customer.Orders)
    .Where(order=>!order.Filled)
    .Where(order=>order.Total >= 100.00m)
    .FirstOrDefault();
if (orderToFill != null) Fill(orderToFill);

故事的寓意:循环以牺牲业务逻辑为代价强调控制流程。不要试图将越来越复杂的控制流堆叠在一起,而是尝试重构代码,以便明确业务逻辑。

答案 1 :(得分:12)

我个人尝试以避免在这里使用goto,只需将循环放入不同的方法 - 虽然你不能轻易突破特定级别的循环,你可以轻松地在任何时候从方法返回。

根据我的经验,这种方法通常会导致更简单,更易读的代码(通常用一个特定的工作)。

答案 2 :(得分:11)

让我们直截了当:使用goto语句没有任何根本性的错误,它不是邪恶的 - 它只是工具箱中的另一个工具。 你如何使用真的很重要,很容易被滥用。

打破某些描述的嵌套循环可以是对该语句的有效使用,尽管您应该首先查看它是否可以重新设计。您的循环退出表达式可以重写吗?你使用适当类型的循环? 您是否可以过滤您可能要迭代的数据列表,以便您不需要提前退出?您是否应该将某些循环代码重构为单独的函数?

答案 3 :(得分:4)

IMO在不支持break n;的语言中可以接受n指定应该突破的循环数。
至少它比设置变量更可读,然后在外循环中检查它。

答案 4 :(得分:2)

我相信'goto'在这种情况下是可以接受的。不幸的是,C#不支持任何突破嵌套循环的好方法。

答案 5 :(得分:2)

这在C#中是一种不可接受的做法。如果您的设计无法避免它,那么,必须使用它。但首先要耗尽所有其他选择。它将提高可读性和可维护性。就你的例子而言,我已经制定了一个这样的潜在重构:

void Original()
{
    int i = 0;            
    while(i <= 10)
    {
        Debug.WriteLine(i);
        i++;
        if (Process(i))
        {
            break;
        }
    }
}

bool Process(int i)
{
    for(int j = 0; j < 3; j++)
        if (i > 5)
        {
            return true;
        }
    return false;
}

答案 6 :(得分:0)

如果您想跳过这一项,我建议使用continue;如果您想退出循环,我建议使用break。对于更深层次的嵌套,将其放在方法中并使用return。我个人宁愿使用状态bool而不是goto。而是使用goto作为最后的手段。

答案 7 :(得分:0)

匿名函数

您几乎总是可以将内部循环破坏到匿名函数或lambda。在这里,您可以看到函数曾经是内部循环的地方,我不得不在其中使用GoTo。

private void CopyFormPropertiesAndValues()
{
    MergeOperationsContext context = new MergeOperationsContext() { GroupRoot = _groupRoot, FormMerged = MergedItem };
    // set up filter functions caller
    var CheckFilters = (string key, string value) =>
    {
        foreach (var FieldFilter in MergeOperationsFieldFilters)
        {
            if (!FieldFilter(key, value, context))
                return false;
        }
        return true;
    };
    // Copy values from form to FormMerged 
    foreach (var key in _form.ValueList.Keys)
    {
        var MyValue = _form.ValueList(key);
        if (CheckFilters(key, MyValue))
            MergedItem.ValueList(key) = MyValue;
    }
}

在手动搜索数据集中的多个项目时也经常发生。可悲的是正确的使用goto比布尔/标志,从清晰的角度来看更好,但是这是比任何更加清晰,避免你的同事的嘲弄。

对于高性能情况,goto是合适的,但是只有1%,在这里说实话...

答案 8 :(得分:-1)

int i = 0;                 
while(i <= 10)     
{         
    Debug.WriteLine(i);         
    i++;         
    for(int j = 0; j < 3 && i <= 5; j++)             
    {
        //Whatever you want to do
    }
 }      

答案 9 :(得分:-2)

C#中不可接受。

将循环包装在函数中并使用return

编辑:在SO上,downvoting用于不正确的答案,而不是你不同意的答案。由于OP明确要求“它是否可接受?”,回答“不可接受”并非不正确(尽管您可能不同意)。