有关C#短路评估的最佳实践是什么?


What is the best practice concerning C# short-circuit evaluation?

另一个话题的评论中的答案和随后的辩论促使我问:

在C中,和&;分别是逻辑运算符和&;的短路版本。

示例用法:

1
2
3
4
if (String.IsNullOrEmpty(text1) | String.IsNullOrEmpty(text2) | String.IsNullOrEmpty(text3))
{
    //...
}

对比:

1
2
3
4
if (String.IsNullOrEmpty(text1) || String.IsNullOrEmpty(text2) || String.IsNullOrEmpty(text3))
{
    //...
}

在编码实践中,哪一个更好用?为什么?

注:我确实认识到这个问题与这个问题类似,但我相信它需要一个特定语言的讨论。


In terms of coding practice which is the better to use and why?

简单答案:总是使用短路版本。根本没有理由不这么做。此外,您可以使代码更清晰,因为您表达了您的意图:逻辑评估。使用按位(逻辑)操作意味着您只需要这样做:位操作,而不是逻辑计算(即使当应用于布尔值时,msdn也会将它们称为"逻辑运算符")。

此外,由于短路只评估需要评估的内容,因此它通常更快,并且允许编写以下代码

1
bool nullorempty = str == null || str.Length == 0;

(请注意,为了解决这个特定的问题,已经存在一个更好的函数,也就是您在问题中使用的string.IsNullOrEmpty)。对于按位逻辑运算,这段代码是不可能的,因为即使strnull,第二个表达式也会被计算,从而产生NullReferenceException

编辑:如果希望在逻辑上下文中发生副作用,请不要使用按位操作。这是一个典型的太聪明的例子。下一个代码维护者(甚至是你自己,几周后)看到这个代码会想"嗯,这个代码可以清除,使用条件运算符",因此不小心破坏了代码。不管谁负责修理这个虫子,我都很遗憾。

相反,如果你不得不依赖副作用,那么就要把它们明确化:

1
2
3
bool hasBuzzed = checkMakeBuzz();
bool isFrobbed = checkMakeFrob();
bool result = hasBuzzed || isFrobbed;

当然,三行而不是一行。结果是代码更加清晰。


我要反过来回答这个问题:我唯一一次使用逻辑运算符是什么时候?

当我有一系列必须全部满足的(低成本)条件时,有时会使用逻辑比较。例如:

1
2
3
4
5
6
7
bool isPasswordValid = true;

isPasswordValid &= isEightCharacters(password);
isPasswordValid &= containsNumeric(password);
isPasswordValid &= containsBothUppercaseAndLowercase(password);

return isPasswordValid;

在我看来,上述内容比:

1
2
3
return (isEightCharacters(password) &&
        containsNumberic(password)  &&
        containsBothUppercaseAndLowercase(password));

缺点是它有点深奥。


当您只关心结果并希望尽快知道结果时,使用&&||,并且即使不满足布尔条件,也没有任何表达式具有必须发生的副作用。也就是说,几乎总是这样。

当必须计算每个表达式时(例如,如果表达式有副作用),请使用&|。但是,由于您不应该有程序所依赖的副作用,即使不满足布尔条件,也必须发生这种副作用,因此您可能不应该使用&|

例如,这可能非常愚蠢:

1
if (false & somethingThatUpdatesTheDatabase()) { /* ... */ }