为什么按位运算符不像逻辑“与或”运算符那样聪明

5
我注意到按位运算不如逻辑“与\或”运算聪明,我想知道为什么?
以下是一个示例:
// For the record
private bool getTrue(){return true;}
private bool getFalse(){return false;}

// Since a is true it wont enter getFalse.
bool a = getTrue() || getFalse(); 

// Since a is false it wont enter getTrue.
bool b = getFalse() && getTrue(); 

// Since b is false it wont enter getTrue.
b = b && getTrue();

然而,位运算符"|="和"&="并不那么智能:

bool a = getTrue();
a |= getFalse(); // a has no chance to get false but it still enters the function.

a = getFalse();
a &= getTrue(); // a has no chance to get true but still performs this operation.

我想知道为什么它们不以相同的逻辑方式工作。
3个回答

10

澄清一下:

当用于bool时,运算符&=|=不是按位运算符 - 它们是逻辑运算符,但它们等同于x = x & yx = x | y,它们不像&&||那样短路。

MSDN中得知:

&操作符在整数操作数上执行按位逻辑AND操作,在布尔操作数上执行逻辑AND操作。

设计人员可以实现||=&&=,但由于它们只适用于布尔类型,所以价值不大。


在C#中,像2 & true这样的操作会发生什么?也就是说,当你正在评估的&具有int和bool操作数时。&被定义为两个int操作数或两个bool操作数,但不是一个int和一个bool,因此需要将其中一个操作数转换为bool或int以使它们匹配,对吗?那么,我的问题是:2 & true会变成int & int,即2 & 1,即0(如果转换为bool则为false),还是bool & bool,即true & true,即true在C#中? - Apriori
@Apriori 2 & true 无法编译。在 C# 中,您不能将 bool 转换为 int 或反之。 - D Stanley

8

D Stanley的回答是正确的;你的错误在于认为应用于bool&是“按位”的。最好将&&&视为逻辑AND的急切和惰性版本。

现在有一个问题,你没有问,但实际上更有趣:

为什么第一次会有非短路版本的bool AND和OR?也就是说,为什么你会说exprX & exprY而不是exprX && exprY

坏的理由是:表达式exprY可能具有你希望始终发生的副作用,而不管exprX的值如何。这是一个不好的理由,因为同时使用表达式的副作用和值是可疑的做法。

好的理由是:计算&可能比计算&&更快。

怎么可能呢?如果我们有时可以避免计算右侧,那么平均来说我们总能节省时间,对吗?

错了错了错了。生成的代码结构为:z = x && y

if x goto CONSEQUENCE
z = false
goto DONE
CONSEQUENCE: z = y
DONE: 

与仅计算 x & y 并将结果分配给 z 相比,这是很多指令,并且大型代码需要更长的时间从磁盘加载,更长的时间进行jit编译,并在处理器缓存中使用更多空间。
此外,这些指令包含一个条件分支和一个非条件分支,显著增加了JIT必须处理的基本块数。 (“基本块”是具有明确开始和结束的代码部分,如果没有异常,则执行基本块中的所有代码。)当它必须分析的基本块数过多时,JIT可能选择放弃某些优化。
最糟糕的是,任何条件分支都会给CPU提供机会,使其分支预测器做出错误的选择,在某些情况下可能会对性能产生严重影响。
现在,这并不是说您永远不应该为bools使用&&; 还没有程序的市场成功完全归因于使用这种微观优化。 我之所以指出这一点,只是因为并不完全清楚为什么在bools上应该有一个非短路逻辑运算符。

非常有趣,这是我过去一直想知道的事情!不过现在我很好奇为什么这个功能被列入“已实现”列表,而不是被搁置在“可有可无”的列表下。 - Ben Aaronson
@BenAaronson:首先,这是一个非常简单的功能;如果您已经有任何二进制运算符,添加一个不难。其次,在一个任意类都可以拥有用户定义的&运算符但bool没有一个的世界里,这将是奇怪的,不是吗? - Eric Lippert
@EricLippert 感谢您的认可 - 现在如果您可以确认我的猜测,即实现 &&=||= 的价值不值得代价,那么我就可以退休了。 :) - D Stanley
1
@DStanley:有趣的是,当我写愚人节关于疯狂复合运算符的帖子时,我甚至没有想到包括&&=||=。http://blogs.msdn.com/b/ericlippert/archive/2011/04/01/compound-assignment-part-two.aspx 这就是它们离我的思维有多远。 :-) - Eric Lippert

2
主要区别在于你是否使用 |/& 或者 ||/&&。后者是短路运算符(也就是所谓的“智能”),而前者则不是(这是处理布尔类型时这些运算符唯一的区别)。由于没有 &&= 或者 ||= 这样的运算符,因此你只会注意到 &=|=,这两个符号并不是短路运算符。
我认为&=|=是存在的,但&&=||=不是因为前两者在数值类型中使用,而短路计算在这种情况下没有意义。

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