为什么&&而不是&

为什么&&&& &|| |最好|

我问了一个编程多年的人,他的解释是:

例如,在if (bool1 && bool2 && bool3) { /*DoSomething*/ }bool1必须是true才能在bool3之前testingbool3 ,如果我使用了单个换句话说,即使他们都必须是真实的,才能进入下一个线路,所以为什么它无论如何都是重要的?

注:我想指出,我是一个孩子的编程等价物,这不是一个严重或紧急的问题,更重要的是理解为什么事情应该以某种方式做,而不是另一种。

在大多数情况下, &&|| 优于&| 因为前者是短路的,这意味着一旦结果清晰,评估就会被取消。

例:

 if(CanExecute() && CanSave()) { } 

如果CanExecute返回false ,则无论CanSave的返回值如何,完整expression式都将为false 。 因此, CanSave不会被执行。

这在以下情况下非常方便:

 string value; if(dict.TryGetValue(key, out value) && value.Contains("test")) { // Do Something } 

如果在字典中找不到提供的键, TryGetValue返回false 。 由于&&的短路性, value.Contains("test")只有在TryGetValue返回truevalue不为null时才会执行。 如果您使用按位AND运算符& ,那么如果在字典中找不到键,则会得到NullReferenceException ,因为expression式的第二部分在任何情况下都会被执行。

一个类似但更简单的例子就是下面的代码(如TJHevel提到的):

 if(op != null && op.CanExecute()) { // Do Something } 

CanExecute仅在op不为null情况下执行。 如果opnull ,则expression式的第一部分( op != null )计算结果为false ,其余部分( op.CanExecute() )的计算结果将被跳过。

除此之外,技术上也是不同的:
&&|| 只能用于bool&| 可以用于任何整型( boolintlongsbyte ,…),因为它们是按位运算符。 &按位AND运算符和|按位或运算符。

确切地说,在C#中,这些运算符( &| [和^ ])被称为“逻辑运算符”(请参阅C#规范 ,第7.11章)。 这些操作符有几个实现:

  1. 整数( intuintlongulong ,章节7.11.1):
    它们被实现来计算操作数和运算符的比特结果,即用于计算按位逻辑AND等。
  2. 列举(第7.11.2章):
    它们被实现为执行枚举的基础types的逻辑操作。
  3. 对于bools和可空bools(第7.11.3和7.11.4章):
    结果不是使用按位计算来计算的。 结果基本上是基于两个操作数的值来查找的,因为可能性的数量太小了。
    由于这两个值都用于查找,因此此实现不是短路。

要非常清楚地解释这意味着什么(即使另一个答案暗示 – 但可能使用术语,你不明白)。

以下代码:

 if (a && b) { Foo(); } 

真的编译到这个:

 if (a) { if (b) { Foo(); } } 

在下面的代码完全按照代表编译的地方:

 if (a & b) { Foo(); } 

这被称为短路。 一般来说,你应该总是使用&&|| 在你的条件下。

奖励标志:有一种情况,你不应该。 如果您处于性能至关重要的情况(这是至关重要的纳秒 ),则必须使用短路(例如, null检查) – 因为短路是分支/跳跃; 这可能会导致分支在CPU上的误预测; 一个&&&便宜得多。 还有一种情况是短路可以实际上破坏逻辑 – 看看我的答案 。

Edit / Diatribe / Monologue :关于最幸福地忽略的分支错误预测。 引用Andy Firth (他已经从事游戏13年):“这可能是人们认为他们需要去的更低的水平,但是他们会错的。了解你正在编程的硬件如何处理分支影响性能到很大程度上……远远超过大多数程序员可能会认识到的:千人死亡。

  • 游戏开发者(以及其他在极端实时环境中工作的人)只要重构其逻辑以更好地适应预测者。 在反编译的mscorlib代码中也有这个证据。
  • 只是因为.net把你从这种types的东西中屏蔽出来并不意味着这并不重要。 60Hz的分支误预测非常昂贵, 或以10000个请求/秒。
  • 英特尔不会有工具来识别错误预测的位置,Windows也不会有这样的performance,也不会有一个词来形容它,这不是一个问题。
  • 对低层次和架构的无知并不能使人们意识到他们的错误。
  • 始终尝试了解您正在使用的硬件的限制。

编辑:这是非信徒的基准。 最好以RealTime / High运行这个进程,以减轻调度程序的影响: https : //gist.github.com/1200737

逻辑运算符(||,&&)与按位运算符(|,&)

逻辑运算符和按位运算符最关键的区别是逻辑运算符需要两个布尔值,并产生一个布尔值,而位运算符需要两个整数并产生一个整数 (注意:整数意味着任何整数数据types,而不仅仅是int)。

为了迂回,按位运算符采用位模式(例如01101011),并对每个位执行按位AND / OR操作。 所以,例如,如果你有两个8位整数:

 a = 00110010 (in decimal: 32+16+2 = 50) b = 01010011 (in decimal: 64+ 16+2+1 = 83) ---------------- a & b = 00010010 (in decimal: 16+2 = 18) a | b = 01110011 (in decimal: 64+32+16+2+1 = 115) 

而逻辑运算符只适用于bool

 a = true b = false -------------- a && b = false a || b = true 

其次,通常可以在bool上使用按位运算符,因为true和false分别相当于1和0,并且如果将true转换为1并将false转换为0,则执行按位运算,然后将非零真,零为假; 如果你只是使用逻辑运算符(检查这个练习),结果会是一样的。

另一个重要的区别是逻辑运算符也是短路的 。 因此,在一些圈子里[1],你经常看到有人这样做:

 if (person && person.punch()) { person.doVictoryDance() } 

这意味着: if person exists (ie is not null), try to punch him, and if the punch succeeds (ie returns true), then do a victory dance

你有没有使用一个按位运算符,而是:

 if (person & person.punch()) { person.doVictoryDance() } 

将转化为: if person exists (ie is not null) and the punch succeeds (ie returns true), then do a victory dance

请注意,在短路逻辑运算符中,如果person为null,则person.punch()代码可能根本不运行。 事实上,在这种特殊情况下,如果person为空,那么第二个代码将产生一个空引用错误,因为无论person是否为null,它都会尝试调用person.punch()。 不评估右操作数的行为称为短路

[1]一些程序员会放弃一个函数调用,这个函数调用在一个ifexpression式中有副作用,而另一些则是一个常见而且非常有用的习惯用法。

由于按位运算符一次只能处理32位数据(如果使用的是32位计算机),如果需要比较大量条件,则会导致更优雅更快的代码,例如

 int CAN_PUNCH = 1 << 0, CAN_KICK = 1 << 1, CAN_DRINK = 1 << 2, CAN_SIT = 1 << 3, CAN_SHOOT_GUNS = 1 << 4, CAN_TALK = 1 << 5, CAN_SHOOT_CANNONS = 1 << 6; Person person; person.abilities = CAN_PUNCH | CAN_KICK | CAN_DRINK | CAN_SIT | CAN_SHOOT_GUNS; Place bar; bar.rules = CAN_DRINK | CAN_SIT | CAN_TALK; Place military; military.rules = CAN_SHOOT_CANNONS | CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT; CurrentLocation cloc1, cloc2; cloc1.usable_abilities = person_abilities & bar_rules; cloc2.usable_abilities = person_abilities & military_rules; // cloc1.usable_abilities will contain the bit pattern that matches `CAN_DRINK | CAN_SIT` // while cloc2.usable_abilities will contain the bit pattern that matches `CAN_PUNCH | CAN_KICK | CAN_SHOOT_GUNS | CAN_SIT` 

对逻辑运算符做同样的操作也需要进行一些比较尴尬的处理:

 Person person; person.can_punch = person.can_kick = person.can_drink = person.can_sit = person.can_shoot_guns = true; person.can_shoot_cannons = false; Place bar; bar.rules.can_drink = bar.rules.can_sit = bar.rules.can_talk = true; bar.rules.can_punch = bar.rules.can_kick = bar.rules.can_shoot_guns = bar.rules.can_shoot_cannons = false; Place military; military.rules.can_punch = military.rules.can_kick = military.rules.can_shoot_guns = military.rules.can_shoot_cannons = military.rules.can_sit = true; military.rules.can_drink = military.rules.can_talk = false; CurrentLocation cloc1; bool cloc1.usable_abilities.can_punch = bar.rules.can_punch && person.can_punch, cloc1.usable_abilities.can_kick = bar.rules.can_kick && person.can_kick, cloc1.usable_abilities.can_drink = bar.rules.can_drink && person.can_drink, cloc1.usable_abilities.can_sit = bar.rules.can_sit && person.can_sit, cloc1.usable_abilities.can_shoot_guns = bar.rules.can_shoot_guns && person.can_shoot_guns, cloc1.usable_abilities.can_shoot_cannons = bar.rules.can_shoot_cannons && person.can_shoot_cannons cloc1.usable_abilities.can_talk = bar.rules.can_talk && person.can_talk; bool cloc2.usable_abilities.can_punch = military.rules.can_punch && person.can_punch, cloc2.usable_abilities.can_kick = military.rules.can_kick && person.can_kick, cloc2.usable_abilities.can_drink = military.rules.can_drink && person.can_drink, cloc2.usable_abilities.can_sit = military.rules.can_sit && person.can_sit, cloc2.usable_abilities.can_shoot_guns = military.rules.can_shoot_guns && person.can_shoot_guns, cloc2.usable_abilities.can_talk = military.rules.can_talk && person.can_talk, cloc2.usable_abilities.can_shoot_cannons = military.rules.can_shoot_cannons && person.can_shoot_cannons; 

使用位模式和位运算符的经典示例是Unix / Linux文件系统权限。

如果是:

 if (obj != null && obj.Property == true) { } 

将按预期工作。

但:

 if (obj != null & obj.Property == true) { } 

可能会抛出一个空引用exception。

简单而简单

1 && 2 = true
因为
1 =真(非零)在c
2 =真(非零)在C

真正的ANDS在逻辑上与真实的给予真实的

1 & 2 = 0 =假
因为
1 = 0001二进制
2 = 0010二进制

0001 ANDS按位与0010给十进制0000 = 0

同样|| 和| 运营商也是..

&&&&的短路版本。

如果我们正在评估false & true ,我们已经从第一个观点看出结果是错误的。 运算符的&&版本将尽快返回结果,而不是评估整个expression式。 还有一个类似于| verion 运算符||

 if (list.Count() > 14 && list[14] == "foo") 

是安全的

 if (list.Count() > 14 & list[14] == "foo") 

如果列表没有合适的大小,将会崩溃。

C#运营商应该解释为什么:

基本上有两个&的或| 这意味着它是有条件的而不是逻辑的,所以你可以分辨两者的区别。

&运算符有一个使用&的例子。

好的,面值

  Boolean a = true; Boolean b = false; Console.WriteLine("a({0}) && b({1}) = {2}", a, b, a && b); Console.WriteLine("a({0}) || b({1}) = {2}", a, b, a || b); Console.WriteLine("a({0}) == b({1}) = {2}", a, b, a == b); Console.WriteLine("a({0}) & b({1}) = {2}", a, b, a & b); Console.WriteLine("a({0}) | b({1}) = {2}", a, b, a | b); Console.WriteLine("a({0}) = b({1}) = {2}", a, b, a = b); 

产生相同的答案。 但是,正如你所展示的那样,如果你有一个更复杂的问题,那么:

 if (a and b and c and d) .. 

如果a不是真的,也许b是一个必须离开的函数,连接到某个东西,得到这个,做那个,做出一个决定。为什么要这么做? 浪费时间, 知道它已经失败了。 为什么要让机器停下来做一些无谓的工作?

我总是用&&因为在没有任何意义的时候,我最可能首先失败,而不是计算。 如果没有办法预测不太可能的select,比如你有一个布尔值来限制数据的输出,比如:

 if (limit && !MyDictionary.ContainsKey("name")) continue; 

如果没有limit ,请不要检查密钥,这可能需要更长的时间。

在逻辑expression式(如if语句)中使用时,由于只要遇到第一个错误结果,就会停止计算expression式。 这是可能的,因为一个错误的值会导致整个expression式是错误的。 类似的(也是在逻辑expression式中) || 是可取的,因为只要遇到真正的expression式就会停止计算expression式,因为任何真值都会导致整个expression式成立。

然而,如果expression式或者ed和ed一起有副作用,并且希望所有这些expression式作为expression式的结果发生(不pipe逻辑expression式的结果如何),那么&| 可用于。 相反, &&|| 运算符可以有效防范不必要的副作用(例如导致抛出exception的空指针)。

&| 运算符也可以和整数一起使用,在这种情况下,它们会产生一个整数结果,这个结果是两个操作数在位级一起被编辑或者排列在一起的。 当整数值的二进制位用作true和false值的数组时,这会很有用。 为了testing一个特定的位是否打开,位掩码是按位进行的。 要打开一点,同一个掩码可以按值进行按位取整。 最后要closures一点,掩码的按位补码(使用~ )按值逐位进位。

 int a = 0; // 0 means all bits off a = a | 4; // set a to binary 100 if ((a & 4) != 0) { // will do something } a = a & (~4) // turn bit off again, a is now 000 

在C#以外的其他语言中,必须注意&和|的逻辑与按位模式。 在上面的代码中, if语句的条件expression式(a & 4) != 0是一种expression这种情况的安全方法,但是在许多C语言中,条件语句可以简单地将零整数值视为假和非零整数值真实的。 (其原因与可用的条件分支处理器指令有关,以及它们与在每个整数操作之后更新的零标志的关系)。因此,可以删除“语句对于零的testing,并且可以将条件缩短为(a & 4)

这可能会导致混乱,甚至可能出现问题,当expression式组合使用位和操作符返回值没有位排列。 考虑下面的例子,其中两个函数的副作用是需要的,在检查它们都是成功的(由它们定义的返回一个非零值):

 if (foo() & bar()) { // do something } 

在C中,如果foo()返回1, bar()返回2,那么由于1 & 2为零,“something”将不会被执行。

C#需要条件语句( if有布尔型oeprand),并且该语言不允许将整型值转换为布尔值。 所以上面的代码会产生编译错误。 这样更准确地expression如下:

 if (foo() != 0 & bar() != 0) { // do something } 

这很重要,因为如果bool2(例如)的评估成本很高,但是bool1是错误的,那么通过使用&& over&

因为&&|| 用于stream量控制 ,就像if/else 。 它并不总是关于条件。 作为一个陈述写作是完全合理的而不是作为一个if或一个条件:

  a() && b() && c() && d(); 

甚至

  w() || x() || y() || z(); 

这不仅仅是因为比相应的if/else版本更容易input。 他们也更容易阅读和理解。

&&和&意思是两个完全不同的东西,给你两个不同的答案。

1 && 2产生1(“真”)
1 & 2产生0(“false”)

&&是一个逻辑运算符,意思是“如果两个操作数都是真的”
&是一个按位比较。 它的意思是“告诉我在两个操作数中设置了哪些位”

最快的(稍微愚蠢的)的方式来解释这个人不需要知道代码的确切操作时,这是

&&正在对这些条件中的每一个进行检查直到find一个假,并将整个结果作为假返回

|| 正在对这些条件中的每一个进行检查, 直到它find一个真,并将整个结果返回为真。

根据所有条件和结果处理MATHS。

| 正在做MATHS基于所有的条件和处理结果。

我从来没有遇到过需要使用|的地方 在if语句中。 我主要使用它来将hex值切分为其组件颜色使用按位移。

例如:

 r = fullvalue >> 0xFF & 0xFF; g = fullvalue >> 0xF & 0xFF; b = fullvalue & 0xFF; 

在这个操作中,“&0xFF”强制只查看二进制值。 我没有亲自find|的用途 尽pipe如此。

只是,

if exp1 && exp2

如果exp1是flase ,则不检查exp2

if exp1 & exp2

如果exp1是false或者true检查exp2

很less有人使用&因为如果exp1是false他们很less想检查exp2

如果你是一个C程序员,请小心 。 C#真的让我感到吃惊。

MSDN为| 运营商:

二进制| 运算符是为整型和布尔值预定义的。 对于整数types,| 计算其操作数的按位或。 对于bool操作数,| 计算其操作数的逻辑或; 也就是说,当且仅当两个操作数都是假时,结果才是错误的。

(强调是我的。)布尔types是专门处理的,在这种情况下,问题才开始有意义,不同的是,正如其他人已经在他们的答案中已经阐明:

&&|| 短路。 &| 评估两个操作数。

什么是最好的取决于副作用,性能和代码可读性等许多事情,但一般来说短路操作者也是可取的,因为他们更好地被像我这样具有类似背景的人所理解。

原因是:我会这样说:由于在C中没有实际的布尔types,所以可以使用按位运算符| 并且在其条件下将其结果评估为真或假。 但是这对C#来说是错误的态度,因为布尔types已经有了一个特例。