C# “一个”中发生了什么;如果(..| |…)”则;及;如果(……)”;内部构造?
我只是想知道在“如果或”和“如果和”中会发生什么。我有一种感觉,它只是使用C# “一个”中发生了什么;如果(..| |…)”则;及;如果(……)”;内部构造?,c#,if-statement,C#,If Statement,我只是想知道在“如果或”和“如果和”中会发生什么。我有一种感觉,它只是使用&&和| |,在内部,所有案例都构建为单个if语句 紧凑形式| |: if(a || b || c) { DoSomething(); } 潜在的内部形式: if(a) { DoSomething(); } else if(b) { DoSomething(); } else if(c) { DoSomething(); } if(a) { if(b) {
&&
和| |
,在内部,所有案例都构建为单个if语句
紧凑形式| |:
if(a || b || c)
{
DoSomething();
}
潜在的内部形式:
if(a)
{
DoSomething();
}
else if(b)
{
DoSomething();
}
else if(c)
{
DoSomething();
}
if(a)
{
if(b)
{
if(c)
{
DoSomething();
}
}
}
紧凑形式&&:
if(a && b && c)
{
DoSomething();
}
潜在的内部形式:
if(a)
{
DoSomething();
}
else if(b)
{
DoSomething();
}
else if(c)
{
DoSomething();
}
if(a)
{
if(b)
{
if(c)
{
DoSomething();
}
}
}
这两个例子的表现有什么不同吗
*编辑:将else添加到| |案例中首先,
|
和&
是。这意味着:
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
}
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
至于守则:
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() && 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
}
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条指令
正如所指出的,性能还取决于条件为真的概率。以他为例: 如果
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%
另外,我建议你读一读“”这本书,它很有趣 使用压缩形式,C#编译器发出的IL将不那么冗长,导致运行时需要处理的指令更少。发出的IL语句及其逻辑实际上是相同的,因此没有处理这种情况或某些特殊指令的内置支持(请记住,您可以将任何带有布尔结果的表达式放入
if
)
对于使用|
运算符(调试生成)的压缩表单:
使用内部表单(考虑到您使用的是else if
而不是if
):
因此,有更多的指令来处理附加的if
语句所需的所有跳转。因此,第一种形式更有效(实际上更可读:)
在性能方面(每种方法用10.000.000次迭代测量了10次,并删除了最高值和最低值,发布版本):
紧凑型:平均55ms
详细格式:平均56ms
所以根本没有什么大区别。代码:
if(a)
if(b)
if(c)
DoSomething();
是逻辑(但不是“实际”)等价物,用于:
if(a && b && c)
DoSomething();
if(a || b || c)
DoSomething();
至于或
操作符,您有点搞错了。逻辑(但同样不是“实际”)等价物,用于:
if(a && b && c)
DoSomething();
if(a || b || c)
DoSomething();
将是:
if(a)
DoSomething();
else if(b)
DoSomething();
else if(c)
DoSomething();
通过实际差异,我理解编译器引入的任何结果代码差异(有关详细信息,请参阅其他答案)。和是条件运算符。它们也是操作符,就像你可能知道的其他操作符一样。(例如,+
,*
,…)
它们的行为类似于逻辑运算符,|
和&
。它们接收两个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()。这种行为称为短路
现在,让我们想想你的例子
if (a || b || c)
DoSomething();
等于
bool value = a || b || c;
if (value)
DoSomething();
bool value = (a || b) || c;
if (value)
DoSomething();
因为条件运算符的值是从左到右的,所以等于
bool value = a || b || c;
if (value)
DoSomething();
bool value = (a || b) || c;
if (value)
DoSomething();
它们的VB等价物可以更具描述性|
在VB中是OrElse
和&
是和。
这些是条件运算符;也就是说,他们让控制子句-如果在您的情况下-根据需要评估条件,而不是总是评估所有条件
例如,在if(a | | b)
中,如果a
为真,那么b
是什么并不重要;结果为真,因此不会对b
进行计算,这将导致更快的执行
此功能也可以用作空检查机制if(a!=null&&a.prop==somevalue)
将防止null引用异常,如果a
为null,如果它不为null,将访问它的prop
属性来评估第二个条件。对于那些阅读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
不可能与块相关联,嵌套的if
,或者实际上除了goto
之外的任何东西都不可能与块相关联,为什么不自己检查一下呢?|
和&
顺便说一下。严格来说,您对|
的解释是错误的-您提供的代码将包含DoSomething()代码>执行三次。考虑<代码> < <代码> >代码> b>代码>和<代码> c>代码>是很长时间以来返回<代码> BOOL 的昂贵方法。那么,很清楚为什么短路操作员应该优先于连续的if
s。但是,即使它们不是方法,但是变量DoSomething()
也会执行三次而不是一次,如果所有的都是真的,这是完全不同的。这个问题的一般形式(问了很多问题,例如,“我应该为结构使用memcpy吗?”)通常归结为“我能确定吗?”