32

假设我有两种方法bool Foo()bool Bar(). 以下哪个更具可读性?

if(Foo())
{
    SomeProperty = Bar();
}
else
{
    SomeProperty = false;
}

或者

SomeProperty = Foo() && Bar();

一方面,我认为短路&&是一个有用的功能,第二个代码示例要短得多。另一方面,我不确定人们通常是否习惯于在&&条件语句之外看到,所以我想知道这是否会引入一些认知失调,使第一个样本成为更好的选择。

你怎么看?是否有其他因素影响该决定?就像,如果&&表达式比屏幕上可以容纳的一行长,我应该更喜欢前者吗?


回答后澄清:

我应该在答案提出的最初问题中包含一些内容。

  • Bar()执行起来可能比 更昂贵Foo(),但是这两种方法都不应该有副作用。
  • 这两个方法的命名都更恰当,不像这个例子。 Foo()归结为类似的东西CurrentUserAllowedToDoX(),更像Bar()是,XCanBeDone()
4

21 回答 21

93

我同意普遍的共识,即 Foo() && Bar() 形式是合理的,除非 Bar() 对它的副作用和价值都有用。

如果 Bar() 对它的副作用和它的价值有用,我的第一选择是重新设计 Bar() 以便产生它的副作用和计算它的值是单独的方法。

如果由于某种原因这是不可能的,那么我会非常喜欢原始版本。对我来说,原始版本更清楚地强调了对 Bar() 的调用是对其副作用有用的语句的一部分。对我来说,后一种形式强调 Bar() 对其价值很有用。

例如,给定之间的选择

if (NetworkAvailable())
  success = LogUserOn();
else
  success = false;

success = NetworkAvailable() && LogUserOn();

我会选择前者;对我来说,很容易忽视后者的重要副作用。

但是,如果可以选择

if (NetworkAvailable())
  tryWritingToNetworkStorage = UserHasAvailableDiskQuota();
else
  tryWritingToNetworkStorage = false;

tryWritingToNetworkStorage = NetworkAvailable() && UserHasAvailableDiskQuota();

我会选择后者。

于 2009-10-09T17:04:48.627 回答
46

假设您的语言允许,我喜欢这种速记符号:

SomeProperty = Foo() ? Bar() : false;
于 2009-10-09T16:43:57.433 回答
19
SomeProperty = Foo() && Bar();

这更具可读性。我不明白怎么会有人坦率地选择另一个。如果 && 表达式长于 1 行,则将其拆分为 2 行...

SomeProperty = 
   Foo() && Bar();

      -or-

SomeProperty = Foo() && 
               Bar();

这几乎不影响可读性和理解恕我直言。

于 2009-10-09T16:44:28.743 回答
13

这取决于 Foo 和 Bar 做什么。

例如,IsUserLoggedIn() && IsUserAdmin()作为 a 肯定会更好&&,但还有一些其他组合(我想不出任何副手) ifs 会更好。

总的来说,我会推荐&&

于 2009-10-09T16:45:23.377 回答
9

两者都不。我将从重命名 SomeProperty、Foo 和 Bar 开始。

我的意思是,您应该构建代码以清楚地传达您的意图。对于不同的功能,我可能会使用不同的形式。然而,就目前而言,任何一种形式都可以。考虑:

IsFather = IsParent() && IsMale();

if (FPUAvailable()) {
    SomeProperty = LengthyFPUOperation();
} else {
    SomeProperty = false;
}

在这里,第一种形式强调逻辑与关系。第二个强调短路。我永远不会用第二种形式写第一个例子。对于第二个示例,我可能更喜欢第二种形式,特别是如果我的目标是清晰。

关键是,用 SomeProperty 和 Foo() 和 Bar() 很难回答这个问题。对于通常的情况,有一些很好的通用答案可以为 && 辩护,但我永远不会完全排除第二种形式。

于 2009-10-09T17:43:45.397 回答
6

你认为人们会以何种方式误解第二个?您是否认为他们会忘记 && 短路,并担心如果在第一个条件为假时调用第二个条件会发生什么?如果是这样,我就不用担心了——他们同样会被以下情况弄糊涂:

if (x != null && x.StartsWith("foo"))

我认为很多人不会将其重写为第一种形式。

基本上,我会选择第二个。使用适当的描述性变量名称(和条件)应该绝对没问题。

于 2009-10-09T16:48:08.627 回答
5

在条件表达式简短而简洁的情况下,就像这种情况一样,我发现第二个更易读。一目了然,您要在这里做什么。第一个例子虽然花了我 2 次通行证来理解发生了什么。

于 2009-10-09T16:43:51.177 回答
4

我会去第二个,但可能会像第一次那样写,然后我会改变它:)

于 2009-10-09T16:44:10.710 回答
3

当我阅读第一个时,并不是很明显 SomeProperty 是一个布尔属性,也不是 Bar() 返回一个布尔值,直到您阅读语句的 else 部分。

我个人的观点是,这种方法应该是一种最佳实践: 每一行代码都应该尽可能地可解释,并尽可能少地参考其他代码

因为语句一要求我引用语句的 else 部分来插入 SomeProperty 和 Bar() 本质上都是布尔值,所以我会使用第二个。

第二,在一行代码中,以下所有事实立即显而易见:

  • SomeProperty 是布尔值。
  • Foo() 和 Bar() 都返回可以解释为布尔值的值。
  • SomeProperty 应该设置为 false,除非 Foo() 和 Bar() 都被解释为 true。
于 2009-10-09T16:56:25.807 回答
3

第一个,它更易于调试

于 2009-10-09T18:02:28.007 回答
1

我认为,最好的方法是使用SomeProperty = Foo() && Bar(),因为它要短得多。我认为任何普通的 .NET 程序员都应该知道 && 运算符的工作原理。

于 2009-10-09T16:45:51.630 回答
1

我会选择长版本,因为乍一看它的作用很清楚。在第二个版本中,您必须停止几秒钟,直到您意识到 && 运算符的短路行为。这是聪明的代码,但不是可读的代码。

于 2009-10-09T16:55:41.063 回答
1

等一下。这些陈述甚至不等同,不是吗?我已经看过他们好几次了,他们对同一件事的评价并不相同。

第一个版本的简写是使用三元运算符,而不是“和”运算符。

SomeProperty = foo() ? bar: false

然而,除了逻辑错误,我同意其他人的观点,即较短的版本更具可读性。

编辑 - 添加

再说一次,如果我错了并且没有逻辑错误,那么第二个方法更具可读性,因为代码在做什么非常明显。

再次编辑 - 添加更多:

现在我看到了。没有逻辑错误。然而,我认为这表明较长的版本对于我们这些还没有喝过咖啡的人来说更清楚。

于 2009-10-09T16:56:53.897 回答
1

如果如您所指, Bar() 有副作用,我更喜欢更明确的方式。否则,有些人可能不会意识到您打算利用短路。

如果 Bar() 是自包含的,那么我会采用更简洁的方式来表达代码。

于 2009-10-09T16:58:57.797 回答
1

如果第一个版本看起来像这样:

if (Foo() && Bar())
{
    SomeProperty = true;
}
else
{
    SomeProperty = false;
}

或像这样:

if (Foo())
{
    if (Bar())
       SomeProperty = true;
    else
       SomeProperty = false;
}
else
{
    SomeProperty = false;
}

那么它至少会是一致的。这种方式的逻辑比第二种情况更难遵循。

于 2009-10-09T16:59:23.220 回答
1

能够单独断点任何特定的函数调用以及将变量的任何特定设置设置为特定值通常很有用——因此,也许有争议的是,我倾向于选择第一个选项。这更有可能允许在所有调试器中进行细粒度断点(在 C# 的情况下,这不是一个很大的数字)。

这样,可以在调用 Foo() 之前、调用 Bar() 之前设置断点(以及变量的集合),以及当变量设置为 false 时——显然同样的断点也可以是用于捕获 Foo() 与 !Foo() 案例,具体取决于人们想到的问题。

当它全部在一条线上时,这并非不可能,但它需要注意可用于找出正在调查的任何问题的原因。

(C# 编译速度很快,因此重新排列代码通常不是什么大问题,但会分散注意力。)

于 2009-10-09T17:50:35.800 回答
0

在我看来,这归结为故意和清晰。

第一种方法让不经意的观察者清楚,除非 Foo() 返回 true,否则您不会执行 Bar()。我知道短路逻辑将阻止 Bar() 在第二个示例中被调用(我自己可能会这样写),但第一种方式乍一看要有意得多。

于 2009-10-09T16:48:22.843 回答
0

AND 行为的短路并不是所有语言的标准,所以如果这对我的代码至关重要,我倾向于谨慎使用它。

我不相信自己会在一天中第五次切换语言后立即看到短路。

因此,如果在 Foo() 返回 false 时永远不应该调用 Boo(),我会选择版本 #2,如果只是作为一种防御性编程技术。

于 2009-10-09T17:55:47.710 回答
0

第一种方法可读性更强,但代码行数更多,第二种方法更有效,没有比较,只有一行!我觉得第二个更好

于 2009-10-09T19:39:38.530 回答
-1

由于您使用的是 c#,我会选择第一个版本或使用三元?:运算符。以这种方式使用&&在 c# 中并不是一个常见的习惯用法,因此您更容易被误解。在其他一些语言中(想到 Ruby),这种&&模式更流行,我想说它在这种情况下是合适的。

于 2009-10-09T17:54:47.777 回答
-3

如果代码对于不了解实际编程语言的人来说是可读的,我会说代码是可读的,因此代码如下

if(Foo() == true and Bar() == true)
then
    SomeProperty = true;
else
    SomeProperty = false;

比可读性强得多

SomeProperty = Foo() && Bar();

如果程序员在你不熟悉后一种语法之后接管代码,那么他将花费你编写这些额外的几个字符所花费的时间的 10 倍。

于 2009-10-09T17:17:30.283 回答