【问题标题】:C#: Nested conditionals vs continue statementC#:嵌套条件与 continue 语句
【发布时间】:2011-03-21 06:29:18
【问题描述】:

在最近使用 ReSharper 时,建议我通过反转 if 条件并使用 continue 语句来减少某些地方的嵌套。

嵌套条件:

foreach(....)
{
    if(SomeCondition)
    {
        //do some things

        if(SomeOtherNestedCondition)
        {
            //do some further things
        }
    }
}

继续声明:

foreach(....)
{
    if(!SomeCondition) continue;

    //do some things

    if(!SomeOtherNestedCondition) continue;

    //do some further things
}

我了解您为什么要减少嵌套以解决性能和内存问题以及两个 sn-ps 如何相互等同的一些逻辑,但是从我的开发背景来看,之前 em> 示例在阅读代码时更容易理解。

您更喜欢哪种方法,为什么?您是否在日常代码中使用 continue 而不是嵌套的 if?

【问题讨论】:

    标签: c# resharper nested-loops continue


    【解决方案1】:

    作为一项规则,我发现最好始终使用除 out 之外的任何条件启动语句块,因为它降低了复杂性,但更重要的是在它们进一步执行之前抛出不兼容的情况,这可以提高代码和内存性能.这还可以通过维护确保您的条件在一段时间内的安全性,从而不太可能将无效场景传递到它们不属于的代码中。

    另外,我认为两者中的第二个个人更具可读性,因为您没有范围层混淆可用的内容,很容易在以后的一层中创建一个变量,而不会意识到它在另一层中不可用,或必须管理它们以进行适当的修改等。

    这不仅仅是循环中的继续,而是指方法应该返回的条件;而不是有一个方法开始

    if (valid)
    {
        do stuff;
    }
    

    它应该总是开始

    if (notValid)
    {
        return;
    }
    

    【讨论】:

    • +1 好点。我和你在这件事上。我通常会将使方法返回/退出的所有条件放在顶部。一般我先抛出异常,然后是 if-return 块,然后是方法逻辑。
    • 反对它的论点是它“结构较少”,因为函数可以结束的方式不止一种。例如,如果您忘记在函数终止之前执行清理代码,您可能会遇到麻烦。也就是说,我很懒,我认为这不是一个大问题,尤其是在 C# 中,清理代码的必要性不如 C++。另外,我更喜欢我的代码缩进更少。
    • 这几乎总是我的论点所以只是为了扮演魔鬼的拥护者我要提到偶尔我想做一些事情(添加日志记录?)就在方法退出之前以及我经常编码的事实这种方式可以使它成为一个真正的PITA。我可能会开始尝试以下策略:如果您返回非错误的 USEFUL 值,请使用单个退出点。
    • @Bill K:在这些事件中,我会编写日志记录方法来返回我想要在错误范围内返回的内容,然后您只需返回 LogAndExit();如果需要一个值,您可以进行通用 return LogAndExit(defaultRetValue);
    • @Jimmy Hoffa 我不是在谈论确切的日志记录,我说的是在 LIKE 日志记录的末尾添加一些功能。例如,在调试期间吐出一行您打算删除的日志记录。其他例子可能是设置一个标志,增加一个计数器或类似的东西。我并不反对你的观点,我一直使用这种模式,我只是提到它偶尔会出现问题。
    【解决方案2】:

    应该没有显着的性能差异,这都是关于可读性的。我个人认为后者更容易阅读。嵌套更少,更易于阅读。

    【讨论】:

      【解决方案3】:

      简答:

      我倾向于使用缩进,这样它意味着发生了一些真正的决策逻辑,即。预期不止一个可能的执行路径的逻辑。

      更长的答案:

      通常更喜欢缩进块而不是前面的“中断”语句(continuebreakreturnthrow)。

      原因: 在我看来,中断语句通常会使代码更难阅读。如果您缩进代码,很容易找到某些决策逻辑发生的位置:这将是缩进较少的代码的第一行。如果您使用带倒置条件的中断语句,则必须做更多工作来了解代码如何分支以及在哪些情况下跳过某些代码。

      对我来说一个值得注意的例外,即在方法的开头验证参数。我将这段代码格式化如下:

      if (thisArgument == null) throw new NullArgumentException("thisArgument");
      if (thatArgument < 0) throw new ArgumentOutOfRangeException("thatArgument");
      ...
      

      原因:由于我不期望这些异常被实际抛出(也就是说,我期望方法被正确调用;调用函数负责检测无效输入,IMO),我不想为不应该发生的事情缩进所有其余代码。

      【讨论】:

        【解决方案4】:

        带有continue 样式代码;您将如何处理不依赖于 SomeOtherNestedCondition 的第三个操作,这使得代码的顺序很重要,而 IMO 使其更难维护。

        例如:

        foreach(....) 
        { 
            if(!SomeCondition) continue; 
        
            //do some things 
        
            if(!SomeOtherNestedCondition) continue; 
        
            //do some further things 
        
            if(!SomeSecondNonNestedCondition) continue;
        
            // do more stuff
        }
        

        如果 SomeOtherNestedCondition 导致 continue; 发生,但 SomeSecondNonNestedCondition 仍应执行,会发生什么情况?

        我会重构出每一段“代码”并使用嵌套的if()s 来调用每个重构的方法,并且我会保留嵌套结构。

        【讨论】:

        • 在这样的过程语言中,代码的顺序总是很重要的。你无法解决这个问题。
        • 我只想在 SomeOtherNestedCondition 之后再添加三分之一继续。如果代码的顺序很重要,那么您最好知道这一点,并且知道正确的顺序 tp 将代码放入...在阅读代码时“看到”它......实际上可以很容易地认为,使用 continue 方法可以“更容易”看到代码 sn-ps 的顺序,因为它们没有隐藏在所有嵌套中...... .
        • +1 好点。是的,我意识到你必须在适当的地方使用它。有时,continue 设计的逻辑过于复杂。
        • 也许我的观点表达得不好,但我认为 KP 明白了重点,有时使用 continue 设计会发生太多事情。
        • 显然顺序很重要,但在我的示例中,如果 SomeSecondNonNestedCondition 应该执行,如果 SomeOtherNestedCondition 为 false,则不会。这可能是也可能不是预期行为,因为 SomeSecondNonNestedCondition 不依赖于 SomeOtherNestedCondition。
        【解决方案5】:

        简单的答案。哪一个更容易阅读和理解。

        【讨论】:

          【解决方案6】:

          结合使用两者。我在循环顶部使用if(condition) continue;if(condition) return; 来验证当前状态,并进一步向下嵌套if 语句来控制我想要完成的任何事情的流程。

          【讨论】:

            【解决方案7】:

            在内存或性能方面没有区别。底层的 IL 只是分支/跳转指令的选择,因此它们是跳转回循环顶部还是跳转到“else”语句并不重要。

            你应该选择更容易阅读的那个。我个人更喜欢避免“继续”,但如果您有许多级别的嵌套,那么您的第二个示例可能更容易遵循。

            【讨论】:

              【解决方案8】:

              使用continues 使大部分代码处于常规程序代码级别。 否则,如果有五次检查,您将根据缩进大小将方法的“肉”缩进 10 或 20 个字符,而这些是 10/20 字符,您必须滚动才能看到较长的行。

              【讨论】:

                【解决方案9】:

                原文continue含义:任何条件为假时,代码不会被处理。下面是一个例子:

                class Program
                {
                    static bool SomeCondition = false;
                    static bool SomeOtherNestedCondition = false;
                    static void Main(string[] args)
                    {    
                        for (int i = 0; i < 2; i++)
                        {
                            if (SomeCondition)
                            {
                                //do some things
                
                                if (SomeOtherNestedCondition)
                                {
                                    //do some further things
                                }
                            }
                            Console.WriteLine("This text appeared from the first loop.");
                        }
                        for (int i = 0; i < 2; i++)
                        {
                            if (!SomeCondition) continue;
                
                            //do some things
                
                            if (!SomeOtherNestedCondition) continue;
                
                            //do some further things
                            Console.WriteLine("This text appeared from the second loop.");
                        }
                        Console.ReadLine(); 
                    }
                }
                

                输出将是:

                【讨论】:

                  【解决方案10】:

                  但是从我的开发背景来看,在阅读代码时,前面的示例更容易理解。

                  嗯 - 我认为,这是您的个人意见。我 - 例如 - 尽量避免这种嵌套,因为在我看来它会使代码更难阅读。

                  如果您更喜欢“之前”版本而不是“之后”版本,请使用它。
                  只需配置 ReSharper,让它建议真正想要它做什么。

                  始终牢记:ReSharper 是一个相当“愚蠢”的重构工具 - 它无法取代开发人员。它只能通过做一些其他愚蠢的复制和粘贴工作来帮助他。
                  即使在 ReSharper 建议相反的重构的情况下,ReSharper 进行的一些重构也会产生结果。

                  因此,不要将 ReSharper 的建议视为最佳实践,而是您可以做的可能性。

                  顺便说一句:您应该在这里考虑性能 - 如果性能方面确实存在显着差异,我会感到惊讶。

                  【讨论】:

                  • 是的,我同意 ReSharper 并不总是做出正确的选择。在这种情况下,我知道转换有效并且是合乎逻辑的。我的问题部分是因为我们的开发团队很小,对此有不同的看法。我很想知道整个 c# 社区倾向于什么。
                  猜你喜欢
                  • 2015-01-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-02-17
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多