C# 条件与(&&)或(||)运算符优先级。

90

我在工作中经常会陷入不必要的编码争论。今天,我问了一下条件AND(&&)或OR(||)哪个优先级更高。我的一个同事坚称它们的优先级相同,我有疑虑,于是我查了一下。

根据MSDN文档,AND(&&)比OR(||)的优先级更高。但是,你能向怀疑的同事证明吗?

http://msdn.microsoft.com/en-us/library/aa691323(VS.71).aspx

bool result = false || true && false; // --> false
// is the same result as
bool result = (false || true) && false; // --> false
// even though I know that the first statement is evaluated as 
bool result = false || (true && false); // --> false

我的问题是如何用代码证明 AND(&&) 的优先级高于 OR(||)?如果你的答案是它并不重要,那么为什么语言中要这样构建呢?


26
这样使用语言是因为他们需要做出一个决定。就我个人而言,我不关心哪个有优先权;我会使用括号以确保得到我期望的结果。重点不在于它为什么这样做,而在于能否在几周、几个月或几年之后回顾代码时弄清楚它在做什么。 - Jeff Siver
28
@Jeff:那不正确。这个决定有一个数学基础,叫做布尔代数 - EFraim
4
翻译:@TheSteve:短路不会影响表达式结果,它只会影响副作用。 - EFraim
11
你需要考虑这一点,这意味着你不应该依赖下一个人知道。如果有任何一个人在办公室可能需要花3秒钟来思考的问题,那么投资0.5秒钟添加一个括号。 - Bill K
39
让我看看是否理解正确。你的同事对语言有一种信仰。这种信仰与规范和实现都相矛盾。你的主张与规范和实现是一致的。那么,为什么要由你来构建证明?负担证明的责任应该落在那个持有荒谬信仰的人身上,而不是那个持有合理信念的人。我不会再浪费时间在这个问题上了;如果他们想花时间去证明假话,就让他们去吧。 - Eric Lippert
显示剩余4条评论
6个回答

138

把第一个false改成true。我知道这样写似乎很愚蠢,因为(true || true),但它能证明你的观点。

bool result = true || true && false;   // --> true 
     result = (true || true) && false; // --> false
     result = true || (true && false); // --> true

13
仅当布尔表达式的结果已知时,短路才会发生;短路不能改变布尔表达式的结果。 - Captain Segfault
7
@James,不会。如果||运算符具有更高的优先级,则表达式将等同于(true || true)&& false => False,而不是true,因为在将{true || true}评估为true后,剩下的是true && false,这不会创建短路。 - Charles Bretana
7
@James,不,如果||运算符具有更高的优先级,则clr会将其解释为(true或true) && false。因此,它首先会计算(true || true)。在这个表达式中,是的,短路发生了,因为对于OR表达式,如果第一部分为真,则无需评估第二部分。因此,这个表达式的值为true。现在我们有了true && false。对于&&操作,只有当第一部分为FALSE时,短路才会发生,在这种情况下它不是,所以A && B必须都为真才能评估为true。因此,它评估了两个部分并得出false。 - Charles Bretana
17
我们能否就停止这些看似反复无常的无意义言论呢?请跟我重复一遍:短路并不会改变结果。仅当结果已知时,短路才适用。短路只影响副作用。 - EFraim
11
这并不是短路的结果,而是由于运算顺序的改变。& 运算符的优先级高于 | 运算符,所以第一个表达式会像 true | (false & false) 一样运算。这会得出 true,因为 false & false = false,接着 false | true = true。然而,| 运算符的优先级高于 && 运算符,所以第二个表达式会像 (true | false) && false 一样运算。这会得出 false,因为 true | false = true,接着 true && false = false - Abion47
显示剩余7条评论

14

如果你真的想吓唬他,可以尝试:

bool result = True() | False() && False();

Console.WriteLine("-----");
Console.WriteLine(result);

static bool True()
{
    Console.WriteLine(true);
    return true;
}

static bool False()
{
    Console.WriteLine(false);
    return false;
}

这将打印出:

True
False
False
-----
False

编辑:

针对评论的回应:

在C#中,|是一个逻辑运算符,执行与||相同的布尔逻辑,但不会短路。此外,在C#中,|运算符的优先级高于||&&

通过输出值,您可以看到如果我使用典型的||运算符,只有第一个True将被打印 - 然后是表达式的结果,该结果也将是True

但由于|的优先级更高,因此首先计算true | false(结果为true),然后false进行&&运算以得出false

我并不是想显示评估顺序,只是想说明当通常不会时,|的右侧仍然被评估了 :)


1
a. 为什么要混合使用布尔和位运算符? b. 为什么坚持打印它?求值顺序与此有何关系? - EFraim
2
回应回应:| 仍然是二进制的。只是对于 bool 类型,它执行所需的操作 - 就像在 C++ 中一样。为了澄清,微软将其记录为逻辑运算符用于 bool 类型。但在我看来,这只会让事情变得更加混乱。 - EFraim
2
这将是一个有趣的恶作剧。 :) - James
1
如果你将 || 更改为 | 并保留 &&,那么表达式的结果会从 true 改变为 false 吗? 因此,通常您要么同时使用 |&,要么使用 ||&& - comecme

6
这样做不就得到你想要的了吗?或者我可能漏掉了什么...
bool result = true || false && false;

这个区分了&&具有更高的优先级和||具有更高的优先级,但不区分||具有更高的优先级和&&和||具有相等的优先级。请记住,如果运算符在优先级上相等,则它们将按照从左到右的顺序进行评估。 - Tyler McHenry
1
我应该说,这确实可以作为C#(或任何普通语言)的证明,因为你实际得到的结果是明确无误的。但如果你遇到一个故意违反布尔代数的奇特语言,它并不是一种通用的方法来解决相对优先级的问题。 - Tyler McHenry

5
你不是用代码证明它,而是用逻辑证明它。AND是布尔乘法,而OR是布尔加法。现在哪个优先级更高?

10
在某些语言中,没有一个函数是这样的(例如Smalltalk)。类比可能会误导。 - Pavel Minaev
11
我不知道其他投票者的想法,但是我(downvoter)之所以投反对票是因为这个回答与问题无关。虽然它恰好符合......但任何人都可以设计一种语言Foo#,使得这不成立;那么你的“证明”怎么用逻辑来解决?这可能会解释为什么选择了这个优先级,但它并不能帮助我确定我安装的编译器是否实际上按照这种方式运行。 - Roman Starkov
3
QED?你知道“证明”某件事是什么意思吗?这种语言可能旨在遵循代数定律,但是如何证明在这个意图和大量工程努力之间,没有人出差错或者在实施优先级规则时误读了规范呢?(虽然不太可能,但并非不可能) - Roman Starkov
2
@EFraim:哪个代数?你假设了一个特定的代数。在理论计算机科学中,正则表达式的通用代数等价于有限状态自动机;根据你的逻辑,.NET正则表达式也应该是这样。但它们不是!Q.E.D.!!! ;-) - Timwi
3
我认为你所说的观点是正确的,只是它并不能被称为“证明”,因为证明比仅仅解释语言设计者如何以及为什么做某事更加严格。“他们基于代数,因此它必须像代数一样工作”并不是一个严格的证明(实际上这样说是逻辑上无效的)。 - Dave Cousineau
显示剩余10条评论

2

false || true && true

结果:true

false && true || true

结果:true


1
无论操作的顺序如何,第一条语句都将返回true。第二条语句仅在||首先被评估时才会返回false。由于它返回true,因此要么&&首先被评估,要么它们具有相同的优先级并且从左到右进行评估。第三个语句 true || true && false(也返回true)仅在&&首先被评估时才能为真。 - CodeHxr

-5

当你的布尔表达式被短路时,你不能仅仅展示最终结果。这里有一段代码片段可以解决你的问题。

它依赖于实现 & 和 | 运算符,这些运算符被 && 和 || 使用,如 MSDN 7.11 Conditional logical operators 所述。

public static void Test()
{
    B t = new B(true);
    B f = new B(false);

    B result = f || t && f;

    Console.WriteLine("-----");
    Console.WriteLine(result);
}

public class B {
    bool val;
    public B(bool val) { this.val = val; }
    public static bool operator true(B b) { return b.val; }
    public static bool operator false(B b) { return !b.val; }
    public static B operator &(B lhs, B rhs) { 
        Console.WriteLine(lhs.ToString() + " & " + rhs.ToString());
        return new B(lhs.val & rhs.val); 
    }
    public static B operator |(B lhs, B rhs) { 
        Console.WriteLine(lhs.ToString() + " | " + rhs.ToString());
        return new B(lhs.val | rhs.val); 
    }
    public override string ToString() { 
        return val.ToString(); 
    }
}

输出应该显示在 || 之前,先评估 &&

True & False
False | False
-----
False

为了更有趣,尝试使用result = t || t && f,并查看短路时会发生什么。


7
短路求值与表达式结果无关,我们能否不再纠结于此? - EFraim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接