为什么更&&
喜欢&
和||
更喜欢|
?
我问过多年来一直在编程的人,他的解释是:
例如,在中if (bool1 && bool2 && bool3) { /*DoSomething*/ }
,bool1
必须是真实的,以便bool2
在继续之前测试哪个必须为真bool3
,等等.如果我使用单个&
而不是测试的顺序,即使所有这些都必须是真的进展到下一行,为什么它至关重要?
注意:我想指出我是幼儿的编程,这不是一个严肃或紧迫的问题.更重要的是理解为什么事情应该以某种方式而不是另一种方式完成.
Dan*_*rth 183
在大多数情况下,&&
并||
优于&
以及|
因为前者短路,这意味着评估只要结果是明确取消.
例:
if(CanExecute() && CanSave())
{
}
Run Code Online (Sandbox Code Playgroud)
如果CanExecute
返回false
,则完整表达式将是false
,无论返回值如何CanSave
.因此,CanSave
不执行.
这在以下情况下非常方便:
string value;
if(dict.TryGetValue(key, out value) && value.Contains("test"))
{
// Do Something
}
Run Code Online (Sandbox Code Playgroud)
TryGetValue
false
如果在字典中找不到提供的密钥,则返回.由于短路性质&&
,value.Contains("test")
仅在执行时TryGetValue
返回true
,因此value
不执行null
.如果你要使用按位AND运算符&
,你会得到一个NullReferenceException
如果在字典中找不到键,因为表达式的第二部分在任何情况下都会被执行.
一个类似但更简单的例子是以下代码(如TJHeuvel所述):
if(op != null && op.CanExecute())
{
// Do Something
}
Run Code Online (Sandbox Code Playgroud)
CanExecute
只有在op
没有的情况下才会执行null
.如果op
是null
,则表达式(op != null
)的第一部分求值为,false
并op.CanExecute()
跳过对rest()的求值.
除此之外,从技术上讲,它们是不同的,太:
&&
和||
只能用于上bool
,而&
并|
可以在任何整型(可使用bool
,int
,long
,sbyte
,...),因为它们是位运算符.&
是按位AND运算符,|
是按位OR 运算符.
准确地说,在C#中,那些运算符(&
,|
[和^
])被称为"逻辑运算符"(参见C#规范,第7.11章).这些运算符有几种实现方式:
int
,uint
,long
和ulong
,章7.11.1):&
是实现计算按位逻辑AND
等.Jon*_*son 82
要非常清楚地解释这意味着什么(即使其他答案暗示它 - 但可能使用你不理解的术语).
以下代码:
if (a && b)
{
Foo();
}
Run Code Online (Sandbox Code Playgroud)
真的编译成这个:
if (a)
{
if (b)
{
Foo();
}
}
Run Code Online (Sandbox Code Playgroud)
以下代码的编译与表示完全相同:
if (a & b)
{
Foo();
}
Run Code Online (Sandbox Code Playgroud)
这称为短路.一般来说,你应该始终使用&&
和||
在你的条件.
奖励标记:有一种情况你不应该.如果您处于性能至关重要的情况(这是纳秒级的关键),则必须在必要时使用短路(例如null
检查) - 因为短路是分支/跳转; 这可能会导致CPU的分支错误预测; 一个&
比便宜得多&&
.还有一种情况是短路实际上可以打破逻辑 - 看看我的这个答案.
Diatribe/Monologue:关于大多数幸福地忽略的分支错误预测.引用Andy Firth(已经从事游戏工作13年):"这可能是人们认为他们需要去的较低级别......但他们错了.了解你为编写分支机构编写的硬件如何处理影响表现到一个巨大的程度......远远超过大多数程序员可能会欣赏的重新:一千次削减致死."
这是非信徒的基准.最好在RealTime/High中运行该过程以减轻具有效果的调度程序:https://gist.github.com/1200737
Lie*_*yan 68
逻辑运算符(||
和&&
)与按位运算符(|
和&
).
逻辑运算符和按位运算符之间最重要的区别是逻辑运算符接受两个布尔值并产生一个布尔值,而按位运算符接受两个整数并产生一个整数(注意:整数表示任何整数数据类型,而不仅仅是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)
Run Code Online (Sandbox Code Playgroud)
而逻辑运算符仅适用于bool
:
a = true
b = false
--------------
a && b = false
a || b = true
Run Code Online (Sandbox Code Playgroud)
其次,通常可以在bool上使用按位运算符,因为true和false分别等于1和0,并且如果将true转换为1并将false转换为0,则执行按位运算,然后转换为非零为真,零到假; 如果你刚刚使用了逻辑运算符,那么结果将是相同的(检查这个运动).
另一个重要的区别是逻辑运算符是短路的.因此,在某些圈子[1]中,您经常会看到人们做这样的事情:
if (person && person.punch()) {
person.doVictoryDance()
}
Run Code Online (Sandbox Code Playgroud)
转换为:"如果人存在(即不是空),试着打他/她,如果拳打成功(即返回真),那就做一个胜利舞蹈".
如果您使用了按位运算符,那么:
if (person & person.punch()) {
person.doVictoryDance()
}
Run Code Online (Sandbox Code Playgroud)
将翻译为:"如果人存在(即不为空)并且拳击成功(即返回true),则进行胜利舞蹈".
请注意,在短路逻辑运算符中,person.punch()
如果person
为null ,则可能根本不运行代码.实际上,在这种特殊情况下,如果person
为null ,则第二个代码将产生空引用错误,因为person.punch()
无论person 是否为null,它都会尝试调用.不评估右操作数的这种行为称为短路.
[1]一些程序员会阻止在if
表达式中放置一个具有副作用的函数调用,而对于其他程序员来说,这是一个常见且非常有用的习惯用法.
由于按位运算符一次以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`
Run Code Online (Sandbox Code Playgroud)
对逻辑运算符执行相同操作需要进行大量的比较:
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;
Run Code Online (Sandbox Code Playgroud)
使用位模式和按位运算符的经典示例是Unix/Linux文件系统权限.
如果是:
if (obj != null && obj.Property == true) { }
Run Code Online (Sandbox Code Playgroud)
会按预期工作.
但:
if (obj != null & obj.Property == true) { }
Run Code Online (Sandbox Code Playgroud)
可能会抛出空引用异常.
归档时间: |
|
查看次数: |
18852 次 |
最近记录: |