如何在循环体中写条件?

时间:2011-04-13 12:34:10

标签: java c++ coding-style

这可能有点主观,但我实际上正在寻找包含一些推理的答案。

我在循环体中遇到了以下两种编程样式。这样:

for (int i = 0; i < myArray.length; i++) {
    if (myArray[i].isEmpty())
        continue;

    doSomeStuff();
    doSomeMoreStuff();
}

而且:

for (int i = 0; i < myArray.length; i++) {
    if (!myArray[i].isEmpty()) {
        doSomeStuff();
        doSomeMoreStuff();
    }
}

我通常使用第一种风格,因为它可以保持缩进水平,尤其是当有多种情况时。但我开始怀疑第二个是否真的更干净。你有一个首选的风格,你能解释一下原因吗?

更新

这是一个更现实的例子。想象一下,我正在读一个像“名字:姓氏”这样的文件,例如:

John;Doe
Joe;Bloggs

这就是我将每一行读入名称对象,忽略空行(可能出现):

while (line = file.readLine()) {
    if (line.isEmpty())
        continue;

    String[] columns = line.split(";");
    names.add(new Name(columns[0], columns[1]));
}

13 个答案:

答案 0 :(得分:4)

到目前为止,我更喜欢第二种,因为我觉得它在语义上更容易阅读。 这使得重构变得更容易。

答案 1 :(得分:3)

事实是,第二次增加cyclomatic complexity并可能导致arrowhead antipattern

此外,保持所有“代码不应该通过此点”检查的开头允许您将它们组合在一起,这意味着它们更容易维护imho。

答案 2 :(得分:2)

这在很大程度上取决于实际代码的样子;如果它本质上更顺序,我更喜欢初始continue如果条件满足,如果它仍然是分支,我使用分支跳过迭代;但是如果嵌套级别变深,我可能会使用continue ...如果你想要一个单一的样式并且总是在每个循环中使用它,我会推荐第二个版本(分支),但是,情境不如continue

答案 3 :(得分:1)

通常取决于条件。我尽量避免否定条件,特别是更复杂的条件。因此,我经常以第一种风格出来。在开头梳理特殊情况并不是那么糟糕,以便人们可以一次性编写一般情况的算法。

答案 4 :(得分:1)

我在循环中间使用continue(或有时breakgoto),但始终将它全部放在一行(因此continue不会意外与if分开,并始终提供“如果...,我们已完成”形式的评论。

评论是一个强大的工具。在这里和那里,十个级别的缩进确实比goto更难阅读。一些控制流结构不会记录自己。

答案 5 :(得分:1)

只是重构。原因是如果MyObject为空,那就是内部调用,它可以自问。没有理由让其他人问MyObject是否为空,只是为了让MyObject做某事。这是属于MyObject的逻辑。你想尽可能地将逻辑放在对象内部,这样它就可以被其他潜在的调用者重用,但是如果你能避免的话,系统的其他部分也不会调用他们没有所有权的东西。它

for(MyObject object : list) {
    object.doABunchOfSimilarThings();
}

....

class MyObject {

    ...

    public void doABunchOfSimilarThings() {
        if(notEmpty()) {
            doThing1();
            doThing2();
            doThing3();        
        }
    }

    ...

}

答案 6 :(得分:0)

对我而言,这取决于循环的内容。

如果循环只包含最多5行和一个条件,我通常会使用第二种样式,因为我发现它可以更容易地看到何时执行代码。

如果有很多嵌套条件(没有else),我更喜欢第一种语法,原因与你大致相同:防止大缩进。使用第二种样式时,每次后续条件检查都会不必要地增加缩进级别,从而降低可读性。

答案 7 :(得分:0)

继续,转到,休息是我们不想使用的第一句话。如果方法有点长,你会发现自己这是一个坏主意。 第二个教训是尽可能使用积极的条件:)

答案 8 :(得分:0)

免责声明:本文中的答案完全反映了回答者的想法,不应被视为普遍共识

对我来说,这是关于可读性的。即使我能理解两个循环体,但有些人可能会花时间理解它们。

第二个循环体仅在数组中的元素不为空时执行,而第一个元素发现数组的元素为空并告诉for循环继续。

第二个循环的可读性使得有意识的调试变得容易,就好像它告诉一个人如果doSomeStuff()doSomeMoreStuff()被调用,该元素永远不会为空

答案 9 :(得分:0)

第二个更好。编程时尽量避免使用continue,break和goto(raptors?)。他们只是拐杖。

答案 10 :(得分:0)

第二个。如果你得到很多缩进级别,你可以通过将块转换为单独的方法来重构。

答案 11 :(得分:0)

怎么样:

for (int i = 0; i < myArray.length; i++) {
    doStuffToValue(myArray[i]);    
}

void doStuffToValue(String value) {
    if (value.isEmpty()) {
        return;
    } 
    doSomeStuff();
    doSomeMoreStuff();
}

通过这种方式,您可以检查值的有效性,而不是循环体。如果需要,您甚至可以为doStuffToValue方法编写单元测试。

答案 12 :(得分:0)

我不认为我曾经在循环中使用过continue,尽管我在函数的开头经常使用return的第一个习语。样式方式continue的部分原因仅适用于需要跳过循环的整个剩余部分的上下文,而if语句无论上下文如何都有效。因此,if往往与代码的其他部分更加一致。

一个不那么主观的原因是,如果你经常跳过整个循环迭代,通常有一种方法可以重构在循环外做出的决定,这会产生性能影响。对于您的示例,我更喜欢使用永远不会有空值的数据结构。

对于性能不成问题的不可避免的复杂事物,我倾向于选择Adriaan将循环体分解为函数的方法。

在这三个原因之间,continue在语法上可行的情况最容易阅读,与使用该数据结构的其他代码一致,而且性能最高的解决方案也不经常出现证明其用途。