【问题标题】:Need help simplifying a nested if block需要帮助简化嵌套的 if 块
【发布时间】:2012-09-27 17:40:01
【问题描述】:

我有以下代码块:

if (x > 5)
{
    if (!DateTime.TryParse(y, out z))
        break;
    if (w.CompareTo(z) == -1)
        break;
}

其中 x 是整数,y 是字符串,z 和 w 是 DateTime 变量。 break; 的原因是整个块都在一个循环中。

有什么方法可以简化它以使其更易于阅读?

【问题讨论】:

  • 我认为你的代码没有问题。它具有足够的可读性,因此不要仅仅因为可以就试图将所有内容都放在一行中。
  • @nemesv 我完全同意。我建议使用说出变量名和简短的注释来描述中断的原因。
  • 实际上我的代码中已经解决了这两点。实际的变量名有点太长了,所以我在发布之前替换了它们;并且上面有一条评论解释了为什么必须打破循环。

标签: c# if-statement simplification


【解决方案1】:

您不需要 multilpe if 块来执行代码,因为您只是在做两件事中的一件,执行循环或不执行循环(一个 if 和一个 else)。如图here 所示,您可以使用单个布尔表达式来表示您是否应该跳过该循环迭代。

(x > 5) && (!DateTime.TryParse(y, out z) || w.CompareTo(z) == -1)

话虽如此,在循环中包含这样的复杂条件会妨碍可读性。就个人而言,我会简单地将这个条件提取到一个方法中,以便循环看起来像这样:

while(!done) // or whatever the while loop condition is
{
    if(itemIsValid(x, y, w, out z))
    {
        //the rest of your loop
    }
}

//it may make sense for x, y, w, and possibly z to be wrapped in an object, or that already may be the case.  Consider modifying as appropriate.
//if any of the variables are instance fields they could also be omitted as parameters
//also don't add z as an out parameter if it's not used outside of this function; I included it because I wasn't sure if it was needed elsewhere
private bool itemIsValid(int x, string y, DateTime w, out DateTime z)
{
    return (x > 5) 
        && (!DateTime.TryParse(y, out z) || w.CompareTo(z) == -1)
}

这有几个优点。首先,它是一种无需 cmets 即可自行记录代码的方式。查看循环时,您可以将其理解为“当我还没有完成时,如果项目有效,请执行所有这些操作”。如果您对如何定义有效性感兴趣,请查看该方法,否则请跳过它。您还可以将方法重命名为更具体的名称,例如“isReservationSlotFree”或它实际代表的任何名称。

如果您的验证逻辑很复杂(这有点复杂),它允许您添加 cmets 和解释,而不会弄乱更复杂的循环。

【讨论】:

  • 我发现您的回答很有帮助,因此我将部分实施。但是,我发现最喜欢的答案是@Justin 提供的答案。如果我可以将您的答案标记为有用,我会的。
  • 我喜欢这个解决方案 - 它简单而优雅。
【解决方案2】:
if (x > 5)
{
    if(!DateTime.TryParse(y,out z) || w.CompareTo(z) == -1)
        break;
}

由于两个条件句的结果相同,所以可以合并为一个。

【讨论】:

  • 你会说对第一个条件做同样的事情会进一步提高它的可读性吗?
  • @Ozzyberto 我在写答案时想到了这一点,但我真的不知道。这是你的判断,但如果是我,我可能会这样做。就个人而言,我讨厌阅读具有一百万种不同条件的条件句。
  • 整个问题是它们都是按这个顺序需要的。如果第一个条件失败,则不应执行任何操作。如果第二个失败,那么第三个也会失败,因为它需要(获取的)值z
  • @Ozzyberto 这实际上根本不是问题。条件运算符都短路了,因此一旦单个失败(考虑到它的结构方式),它甚至都不会费心评估重命名表达式。正如这个答案所示,这不仅仅是性能优化。它还允许您检查 null 值,然后在后续表达式中使用它的成员。
【解决方案3】:
if ((x > 5) && (!DateTime.TryParse(y, out z) || w.CompareTo(z) == -1))
    break;

【讨论】:

  • 虽然它非常简洁但绝对不可读且很难理解。 OP想要“让它更容易阅读”......
  • @nemesv 可读性是主观的。例如,我发现这更容易阅读。无论如何,OP要求简化陈述,这就是这个答案的作用。简化答案是提高还是降低其可读性是另一个(也是主观的)问题。
【解决方案4】:

“简化”并不意味着更容易阅读。

您可以通过以下方式使您的代码更易于阅读(并且在各种编码规则方面更安全):

1) 总是在 if 语句和类似语句中使用括号

2) 避免使用 '!' ('== false' 更加明确)

3) 使用明确这些变量是什么的变量名。

4) 避免使用多个 break 语句。相反,请使用在 while 条件中评估的标志。

5) 如果您的代码仍然难以阅读:使用 cmets !

【讨论】:

  • +1 表示 #2。有时我觉得我是唯一一个写== false的人,我相信它更具可读性,因为!很容易被忽略。
  • 不同的人有不同的代码规则/指南。例如,我认为#1 是指导方针,而不是规则,因为我觉得有有效的例外。我完全不同意#2。其余的不是绝对的,它们已经被列为指导方针。请记住,可读性始终是主观的,因此您不会找到普遍适用的硬性规则。
【解决方案5】:

更重要的是:为w, x, y, z 使用描述性变量名称(希望这些名称仅用于您的示例):

您也可以使用小于或大于运算符来代替CompareTo

if (x > 5)
{
    bool isValidDate = DateTime.TryParse(y, out z);
    if (!isValidDate || z > w)
    {
        // comment like: stop processing if the current date 
        // is after the reference date, or if there was a parsing error
        break;
    }
}

【讨论】:

  • 这正是我一直在寻找的东西。只是一件小事 - 它应该是 z < w 而不是相反。
【解决方案6】:

这里还有一个版本。

var Break = x > 5 ? ((!DateTime.TryParse(y, out z) || w.CompareTo(z) == -1) ? true : false) : false;

简短但妨碍可读性。

【讨论】:

  • 条件运算符解析为真/假的意义何在?它们完全是多余的,可以删除。 example
  • 我在没有它的情况下提供了指向此问题的另一个答案的链接。只需点击链接即可。
【解决方案7】:
if ( x > 5 ){
    if (!DateTime.TryParse(y, out z) || w.CompareTo(z) == -1) break;
}

【讨论】:

    猜你喜欢
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    • 2011-03-15
    相关资源
    最近更新 更多