【问题标题】:What is the best practice concerning C# short-circuit evaluation?关于 C# 短路评估的最佳实践是什么?
【发布时间】:2008-12-11 21:41:08
【问题描述】:

另一个帖子中的答案和随后的debate in the comments 提示我问:

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

示例用法:

if (String.IsNullOrEmpty(text1) | String.IsNullOrEmpty(text2) | String.IsNullOrEmpty(text3))
{
    //...
}

对比:

if (String.IsNullOrEmpty(text1) || String.IsNullOrEmpty(text2) || String.IsNullOrEmpty(text3))
{
    //...
}

就编码实践而言,哪个更好用,为什么?

注意:我确实意识到这个问题与this question 类似,但我相信它需要针对特定​​语言进行讨论。

【问题讨论】:

    标签: c#


    【解决方案1】:

    就编码实践而言,哪个更好用,为什么?

    简单的答案:始终使用短路版本。根本没有理由不这样做。此外,您可以使代码更清晰,因为您表达了 intent:逻辑评估。使用按位(逻辑)运算意味着您只需要:位运算,而不是逻辑求值(即使 MSDN 在应用于布尔值时也称它们为“逻辑运算符”)。

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

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

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

    编辑:如果您希望在逻辑上下文中出现副作用,请仍然不要使用按位运算。这是太聪明的典型例子。代码的下一个维护者(甚至是你自己,几周后)看到这段代码会想“嗯,这段代码可以被清除以使用条件运算符”,从而无意中破坏了代码。我很同情负责修复这个错误的人。

    相反,如果您必须依赖副作用,请明确说明:

    bool hasBuzzed = checkMakeBuzz();
    bool isFrobbed = checkMakeFrob();
    bool result = hasBuzzed || isFrobbed;
    

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

    【讨论】:

    • 这是一个不错的答案,但是您可以将“按位”运算符更改为“逻辑”,将“逻辑”更改为“条件”吗?这是 MSDN 使用的术语。从技术上讲,两者都是“合乎逻辑的”,但加倍版本也短路了
    • “条件与运算符 (&&) 对其布尔操作数执行逻辑与,但仅在必要时评估其第二个操作数。” "msdn.microsoft.com/en-us/library/2a723cdk(VS.71).aspx"
    • 完美答案。 +1 用于副作用讨论——同意,为了维护,总是明确的。 3 个 LOC 与维护噩梦,3 个 LOC 胜出。
    • @Jimmy:您的链接或多或少与您的术语主张相矛盾。实际上,我认为您在某种程度上是对的:当应用于布尔值时,MSDN 调用两个运算符logical。我会在我的回答中反映这一点。
    • 另外,即使您不认为 SC 评估是可行的方法,也请这样做,因为人们已经以这种方式编写 C/C++ 30 年了。现在几乎是普通法了。
    【解决方案2】:

    我将反过来回答这个问题:我在什么时候使用逻辑运算符?

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

    bool isPasswordValid = true;
    
    isPasswordValid &= isEightCharacters(password);
    isPasswordValid &= containsNumeric(password);
    isPasswordValid &= containsBothUppercaseAndLowercase(password);
    
    return isPasswordValid;
    

    在我看来,上面的内容比:

    return (isEightCharacters(password) &&
            containsNumberic(password)  &&
            containsBothUppercaseAndLowercase(password));
    

    缺点是它有点深奥。

    【讨论】:

    • 啊,按位赋值运算符的有趣用法。谢谢!
    • 严格来说,如果密码为 7 个字符,则无效。因此,它是否包含数字和/或大写/小写无关紧要,也不会改变任何内容。确实应该应用短路。如果单个返回语句的可读性是一个问题,您可以将代码更改为三行:if (!RequirementCheck(input)) return false;,那么最后一行应该只是说return true;。但是,如果你想要每个 req.选中返回特定消息,然后合并消息,那么这个例子不再适用于这个问题,我们有更多的无关紧要。
    • 我有时在循环中以这种方式使用按位赋值运算符,例如。 int sum = 0; bool anyOdd = false; foreach (int number in GetNumbers()) { sum += number; anyOdd |= number % 2 == 1; } 但在这个答案中,我同意 @Suamere 的观点,即短路更清洁。
    【解决方案3】:

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

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

    例如,这可能非常愚蠢:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-10
      • 2015-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多