假设您使用的是不带C语言的语言,&&
并且需要执行与问题中相同的代码。
您的代码为:
if(smartphone != null)
{
if(smartphone.GetSignal() > 50)
{
// Do stuff
}
}
这种模式会出现很多。
现在想象一下我们假设语言的2.0版引入了&&
。想想你觉得那有多酷!
&&
是我上面的示例所做的公认的惯用方法。使用它不是坏习惯,实际上,在上述情况下不使用它是坏习惯:有这样一种语言的经验丰富的编码人员会想知道else
过去或其他原因不按常规方式工作。
该语言很聪明,因为它知道如果第一条语句为假,那么即使评估第二条语句也没有意义,因此永远不会抛出空引用异常。
不,您很聪明,因为您知道如果第一条语句为假,那么即使评估第二条语句也没有意义。语言像一盒石头一样愚蠢,并且按照您的指示去做。(约翰·麦卡锡(John McCarthy)更加聪明,他意识到短路评估对于语言来说将是一件有用的事情)。
聪明与语言聪明之间的区别很重要,因为好的习惯和坏习惯常常归结为您在必要时会变得聪明,但不再聪明。
考虑:
if(smartphone != null && smartphone.GetSignal() > ((range = (range != null ? range : GetRange()) != null && range.Valid ? range.MinSignal : 50)
这会将您的代码扩展为检查range
。如果the range
为null,则尝试通过调用将其设置,GetRange()
尽管可能会失败,所以range
可能仍为null。如果range在此之后不为null,则使用Valid
其MinSignal
属性,否则使用默认值50
。
这也取决于&&
,但是将其放在一行中可能太聪明了(我不能100%地确定我做对了,并且我不会再次检查,因为事实证明了我的意思)。
虽然这不是&&
这里的问题,但是使用它在一个表达式中添加很多内容的能力(一件好事)增加了我们不必要地编写难以理解的表达式的能力(一件坏事)。
也:
if(smartphone != null && (smartphone.GetSignal() == 2 || smartphone.GetSignal() == 5 || smartphone.GetSignal() == 8 || smartPhone.GetSignal() == 34))
{
// do something
}
在这里,我将结合使用&&
和检查某些值。对于电话信号,这是不现实的,但在其他情况下,确实会出现这种情况。在这里,这是我不够聪明的一个例子。如果执行以下操作:
if(smartphone != null)
{
switch (smartphone.GetSignal())
{
case 2: case 5: case 8: case 34:
// Do something
break;
}
}
我不仅在可读性方面而且在性能方面都有所提高(对的多个调用GetSignal()
可能没有被优化)。
这里的问题还不&&
只是拿起那把特定的锤子,而把其他东西都看成是钉子。不使用它会让我做得比使用它更好。
最后一个偏离最佳实践的情况是:
if(a && b)
{
//do something
}
相比:
if(a & b)
{
//do something
}
关于为什么我们会偏爱后者的经典论点是,在评估b
我们想要是否a
为真时有一些副作用。我不同意这一点:如果这种副作用是如此重要,请在单独的代码行中实现它。
但是,就效率而言,两者中的任何一个都可能更好。b
前者显然将执行更少的代码(根本不会在一个代码路径中进行评估),这可以为我们节省评估所需的时间b
。
尽管第一个也有一个分支。考虑我们是否使用假设的C风格语言将其重写为no &&
:
if(a)
{
if(b)
{
// Do something
}
}
这笔额外费用if
在我们对的使用中隐藏了&&
,但仍然存在。因此,在这种情况下,会发生分支预测,因此可能会发生分支错误预测。
因此,if(a & b)
在某些情况下代码可以更有效。
在这里,我要说if(a && b)
的仍然是开始时的最佳实践方法:更常见的是,在某些情况下唯一可行的方法(b
如果a
为假则将出错),并且通常更快。值得注意的是,if(a & b)
在某些情况下,这通常是有用的优化。