我只是想知道“如果OR ”和“如果AND ”内部会发生什么。我觉得使用&&
和||
只是syntactic sugar,并且在内部所有情况都构建为单个if语句。
紧凑形式||:
if(a || b || c)
{
DoSomething();
}
潜在的内部形式:
if(a)
{
DoSomething();
}
else if(b)
{
DoSomething();
}
else if(c)
{
DoSomething();
}
紧凑形式&&:
if(a && b && c)
{
DoSomething();
}
潜在的内部形式:
if(a)
{
if(b)
{
if(c)
{
DoSomething();
}
}
}
这两个例子的表现有什么不同吗?
*编辑:将其他内容添加到||情况下
答案 0 :(得分:33)
首先,||
和&&
是short-circuit。这意味着:
if(a || b || c)
DoSomething();
如果a
为真,则不会评估b
和c
。
其次,您对||
的实施是错误的:
if(a)
DoSomething();
if(b)
DoSomething();
if(c)
DoSomething();
DoSomething()
将被称为最多3次。
应该是:
if(a)
DoSomething();
else if(b)
DoSomething();
else if(c)
DoSomething();
要完成,如果您希望性能在您的条件中首先选择较短的呼叫:
if(aShortFunctionToExecute() || aVeryVeryLongFunctionToExecute())
DoSomething();
会比
更快if(aVeryVeryLongFunctionToExecute() || aShortFunctionToExecute())
DoSomething();
如果您反汇编代码:
private static void Main()
{
if (a() && b() && c())
{
Console.WriteLine("DoSomething");
}
}
bool a(){
return true;
}
bool b(){
return 3 % 2 == 1;
}
bool c(){
return (3 % 2) / 1 == 1;
}
你会得到:
if (a() && b() && c())
00000022 call FFFFFFFFFFEE8D90
00000027 mov byte ptr [rbp+20h],al
0000002a movzx eax,byte ptr [rbp+20h]
0000002e test eax,eax
00000030 je 000000000000005A
00000032 call FFFFFFFFFFEE8D98
00000037 mov byte ptr [rbp+21h],al
0000003a movzx eax,byte ptr [rbp+21h]
0000003e test eax,eax
00000040 je 000000000000005A
00000042 call FFFFFFFFFFEE8DA0
00000047 mov byte ptr [rbp+22h],al
0000004a movzx ecx,byte ptr [rbp+22h]
0000004e xor eax,eax
00000050 test ecx,ecx
00000052 sete al
00000055 mov dword ptr [rbp+24h],eax
00000058 jmp 0000000000000062
0000005a nop
0000005b mov dword ptr [rbp+24h],1
00000062 nop
00000063 movzx eax,byte ptr [rbp+24h]
00000067 mov byte ptr [rbp+2Fh],al
0000006a movzx eax,byte ptr [rbp+2Fh]
0000006e test eax,eax
00000070 jne 0000000000000087
{
00000072 nop
Console.WriteLine("DoSomething");
00000073 mov rcx,12603398h
0000007d mov rcx,qword ptr [rcx]
00000080 call 00000000577A82A0
00000085 nop
}
代码:
private static void Main()
{
if (a())
if(b())
if(c())
Console.WriteLine("DoSomething");
}
static bool a(){
return true;
}
static bool b(){
return 3 % 2 == 1;
}
static bool c(){
return (3 % 2) / 1 == 1;
}
你会得到:
if (a())
00000022 call FFFFFFFFFFEE8D90
00000027 mov byte ptr [rbp+20h],al
0000002a movzx ecx,byte ptr [rbp+20h]
0000002e xor eax,eax
00000030 test ecx,ecx
00000032 sete al
00000035 mov dword ptr [rbp+24h],eax
00000038 movzx eax,byte ptr [rbp+24h]
0000003c mov byte ptr [rbp+3Fh],al
0000003f movzx eax,byte ptr [rbp+3Fh]
00000043 test eax,eax
00000045 jne 00000000000000A4
if(b())
00000047 call FFFFFFFFFFEE8D98
0000004c mov byte ptr [rbp+28h],al
0000004f movzx ecx,byte ptr [rbp+28h]
00000053 xor eax,eax
00000055 test ecx,ecx
00000057 sete al
0000005a mov dword ptr [rbp+2Ch],eax
0000005d movzx eax,byte ptr [rbp+2Ch]
00000061 mov byte ptr [rbp+3Fh],al
00000064 movzx eax,byte ptr [rbp+3Fh]
00000068 test eax,eax
0000006a jne 00000000000000A4
if(c())
0000006c call FFFFFFFFFFEE8DA0
00000071 mov byte ptr [rbp+30h],al
00000074 movzx ecx,byte ptr [rbp+30h]
00000078 xor eax,eax
0000007a test ecx,ecx
0000007c sete al
0000007f mov dword ptr [rbp+34h],eax
00000082 movzx eax,byte ptr [rbp+34h]
00000086 mov byte ptr [rbp+3Fh],al
00000089 movzx eax,byte ptr [rbp+3Fh]
0000008d test eax,eax
0000008f jne 00000000000000A4
Console.WriteLine("DoSomething");
00000091 mov rcx,125D3398h
0000009b mov rcx,qword ptr [rcx]
0000009e call 00000000577B82A0
000000a3 nop
这有点长:它需要40条指令而不是31条。
正如thanosqr所指出的那样,表现还取决于你的情况成立的概率。举个例子:
如果a
在99%的时间内失败并需要1秒才能运行且b
99%的时间都会成功并且需要10秒才能运行,超过100次尝试你会先加快b
的速度:
if(b || a) => 10s 99% ==> 100 runs will take 99*10+11 = 1001s
if(b || a) => 11s 1%
if(a || b) => 11s 99% ==> 100 runs will take 99*11+1 = 1090s
if(a || b) => 1s 1%
另外,我建议你这个阅读" Why is it faster to process a sorted array than an unsorted array?"这很有意思!
答案 1 :(得分:7)
使用紧凑形式,C#编译器发出的IL将不那么冗长,导致在运行时处理的指令更少。发出的IL语句及其逻辑实际上是相同的,因此没有花哨的内置支持来处理该情况或一些特殊指令(请记住,您可以将任何表达式与布尔结果到if
)。
对于使用||
运算符的紧凑形式(调试版本):
.method private hidebysig static void One() cil managed
{
// Code size 38 (0x26)
.maxstack 2
.locals init ([0] bool CS$4$0000)
IL_0000: nop
IL_0001: ldsfld bool ConsoleApplication4.Program::a
IL_0006: brtrue.s IL_0019
IL_0008: ldsfld bool ConsoleApplication4.Program::b
IL_000d: brtrue.s IL_0019
IL_000f: ldsfld bool ConsoleApplication4.Program::c
IL_0014: ldc.i4.0
IL_0015: ceq
IL_0017: br.s IL_001a
IL_0019: ldc.i4.0
IL_001a: nop
IL_001b: stloc.0
IL_001c: ldloc.0
IL_001d: brtrue.s IL_0025
IL_001f: call void ConsoleApplication4.Program::DoSomething()
IL_0024: nop
IL_0025: ret
} // end of method Program::One
使用您的内部表单(考虑到您使用的是else if
而不是if
):
.method private hidebysig static void Two() cil managed
{
// Code size 60 (0x3c)
.maxstack 2
.locals init ([0] bool CS$4$0000)
IL_0000: nop
IL_0001: ldsfld bool ConsoleApplication4.Program::a
IL_0006: ldc.i4.0
IL_0007: ceq
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: brtrue.s IL_0015
IL_000d: call void ConsoleApplication4.Program::DoSomething()
IL_0012: nop
IL_0013: br.s IL_003b
IL_0015: ldsfld bool ConsoleApplication4.Program::b
IL_001a: ldc.i4.0
IL_001b: ceq
IL_001d: stloc.0
IL_001e: ldloc.0
IL_001f: brtrue.s IL_0029
IL_0021: call void ConsoleApplication4.Program::DoSomething()
IL_0026: nop
IL_0027: br.s IL_003b
IL_0029: ldsfld bool ConsoleApplication4.Program::c
IL_002e: ldc.i4.0
IL_002f: ceq
IL_0031: stloc.0
IL_0032: ldloc.0
IL_0033: brtrue.s IL_003b
IL_0035: call void ConsoleApplication4.Program::DoSomething()
IL_003a: nop
IL_003b: ret
} // end of method Program::Two
因此,有更多指令可以处理额外if
语句所需的所有跳转。因此,第一种形式更有效(实际上更具可读性:))。
在性能方面(每种方法使用10.000.000次迭代测量10次并删除最高和最低值,发布版本):
紧凑形式:平均55ms
详细形式:平均56毫秒
所以没有太大的区别。
答案 2 :(得分:3)
对于那些阅读C#而不是汇编的人来说,真正的内部形式更接近:
if(a) goto yes;
if(b) goto yes;
if(c) goto yes;
goto no;
yes: DoSomething();
goto done;
no: /* if there were an else it would go here */;
done: ;
的
if(a || b || c)
DoSomething();
和
if(!a) goto no;
if(!b) goto no;
if(!c) goto no;
yes: DoSomething();
goto done;
no: /* if there were an else it would go here */;
done: ;
的
if(a && b && c)
DoSomething();
那是因为实际指令是条件分支 - 在内部形式中,if
不可能与块,嵌套if
或实际上除{{{}}之外的任何内容相关联。 1}}。
答案 3 :(得分:2)
代码:
if(a)
if(b)
if(c)
DoSomething();
是一个逻辑(但不是“实际”)的等价物:
if(a && b && c)
DoSomething();
对于OR
运算符,你有点错误。一个逻辑(但同样不是“实际”)的等价物:
if(a || b || c)
DoSomething();
将是:
if(a)
DoSomething();
else if(b)
DoSomething();
else if(c)
DoSomething();
根据实际差异,我理解编译器引入的任何结果代码差异(有关详细信息,请参阅其他答案)。
答案 4 :(得分:2)
||
和&&
是条件运算符。他们也是运营商,就像你可能知道的其他运营商一样。 (例如+
,*
,...)
他们的行为类似于逻辑运算符|
和&
。他们收到两个bool
类型变量并以这种方式返回bool
值:
// If one of them is true, the return value is true. Otherwise, it's false.
true | true == true
true | false == true
false | true == true
false | false == false
// If both of them are true, the return value is true. Otherwise, it's false.
true & true == true
true & false == false
false & true == false
false & false == false
然而,对于条件运算符,存在一点差异:短路。
假设此代码:
bool func1() { .. }
bool func2() { .. }
bool b1 = func1() || func2();
bool b2 = func1() && func2();
如果func1()
返回true
,b1
将变为true
,无论func2()
返回什么。因此,我们不需要致电func2()
而实际上不需要。如果func1()
返回false
,则同样的内容会应用于b2
。此行为称为短路。
现在,让我们考虑一下你的例子。
if (a || b || c)
DoSomething();
等于
bool value = a || b || c;
if (value)
DoSomething();
由于条件运算符的order of evaluation是从左到右,它等于
bool value = (a || b) || c;
if (value)
DoSomething();
答案 5 :(得分:1)
他们的VB等价物可以更具描述性。 VB中||
为OrElse
,&&
为AndAlso
这些是条件运算符;这意味着他们在你的案例中制定控制条款 - if
- 根据需要评估条件而不是所有条件。
例如,在if ( a || b )
a
如果b
为真,b
是什么并不重要;结果为true,因此if ( a != null && a.prop == somevalue )
将不会被评估,这将导致更快的执行。
此功能也可用作空值检查机制。如果a
为空,prop
将阻止空引用异常,如果它不为空,则将访问其{{1}}属性以评估第二个条件。