代码示例:
int var_a;
//...
//Some code that fetches var_a from db if db field is not null
//...
// region 1
if(var_a != null && var_a > 0) // do something
//region 2
if(var_a != null){
if(var_a>0) // do something
}
问题1: C#的区域1和2之间有什么区别吗?:
问题2:在这种情况下,所有编译器/解释器的行为是否相同?
注意:我知道有一些int.tryparse()。我只是想了解编译器在这种情况下是如何工作的。
注2:有人卡住编译器必须给出错误,让我解释一下:
class Test
{
int a;
private void Test()
{
int b=99;
if (this.a != null && a > 0) b = 100;
}
private void Fill_A()
{
this.a = 6;
}
}
编译器没有错误。所以我们绕过了编译器。如果我们执行代码:
Test();
b是99
Fill_A();
Test();
b是100.
整数不是我们关注的问题。所以我希望现在我们可以专注于问题(:
答案 0 :(得分:7)
问题1 :不,没有功能差异。 IL 可以不同,但如果有的话,它会很小(并且在下一个规范/编译器中产生的IL可能会发生变化)
问题2 :没有看过每个C#编译器或解释器的源代码,可能无法知道。同样,有效的编译器/解释器将生成功能相同的代码。
请注意,您的示例是无意义的,int
永远不会为空。无论如何,嵌套而不是&&将提供相同的行为。
答案 1 :(得分:2)
好的,以你想要的方式回答你的问题,因为你很难。如果你想要一个不同的答案,我强烈建议你实际问一个你想要回答的问题。
我正在编译此代码,首先只有区域1,然后是第二个只有区域2:
int var_a = 0;
//...
//Some code that fetches var_a from db if db field is not null
//...
// region 1
if(var_a != null && var_a > 0) var_a = -1;
//region 2
if(var_a != null){
if (var_a > 0) var_a = -1;
}
如果我提取区域1的IL代码,我会得到:
IL_0015: nop
IL_0016: ldc.i4.0
IL_0017: stloc.0
IL_0018: ldloc.0
IL_0019: ldc.i4.0
IL_001a: cgt
IL_001c: ldc.i4.0
IL_001d: ceq
IL_001f: stloc.1
IL_0020: ldloc.1
IL_0021: brtrue.s IL_0025
IL_0023: ldc.i4.m1
IL_0024: stloc.0
IL_0025: ldloc.0
对于区域2,我明白了:
IL_0015: nop
IL_0016: ldc.i4.0
IL_0017: stloc.0
IL_0018: ldc.i4.1
IL_0019: ldc.i4.0
IL_001a: ceq
IL_001c: stloc.1
IL_001d: nop
IL_001e: ldloc.0
IL_001f: ldc.i4.0
IL_0020: cgt
IL_0022: ldc.i4.0
IL_0023: ceq
IL_0025: stloc.1
IL_0026: ldloc.1
IL_0027: brtrue.s IL_002b
IL_0029: ldc.i4.m1
IL_002a: stloc.0
IL_002b: nop
IL_002c: ldloc.0
所以,是的,有一点点差异。 JetBrains DotPeek显示出不同。
区域1:
if (num > 0)
num = -1;
区域2:
int num = 0;
bool flag = 1 == 0;
if (num > 0)
num = -1;
JustDecompile清理了一些东西,并显示了两者的IL-> C#转换:
if (var_a > 0)
{
var_a = -1;
}
由于您非常关注效率,我编写了一些代码来尝试对差异进行基准测试:
Random rn = new Random();
List<int> l = new List<int>();
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
for (int j = 1; j <= 20; ++j)
{
l.Clear();
sw.Start();
if (j % 2 == 0)
{
Console.Write("A: ");
for (int i = 0; i < 100000000; ++i)
{
int var_a = rn.Next(1, 10000) * (rn.NextDouble() <= 0.5 ? -1 : 1);
if (var_a != null)
if (var_a > 0) var_a *= -1;
l.Add(var_a);
}
}
else
{
Console.Write("B: ");
for (int i = 0; i < 100000000; ++i)
{
int var_a = rn.Next(1, 10000) * (rn.NextDouble() <= 0.5 ? -1 : 1);
if (var_a != null && var_a > 0) var_a *= -1;
l.Add(var_a);
}
}
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
sw.Reset();
}
A的值:
2918.6503
2910.8609
2916.2404
2909.5394
2914.0309
2961.0775
2948.4139
2957.1939
2962.1737
B的值:
3170.8064
2891.6971
2924.8533
2890.6248
2885.1991
2890.6321
2887.0145
2935.6778
2909.035
A对B的整个100,000,000执行周期的平均值分别为2933 ms
和2932 ms
。
每次执行内部块时,2.9331 * 10^-5
vs 2.9317 * 10^-5
。
现在我们已经解决了这个问题,我不得不问你为什么要用C#语言编写一个高级语言的程序,因为你关心的是某种方式使得0.000000014045333333 ms
在一种方式与另一种方式之间产生差异。也许你应该尝试像汇编这样更低级别的东西?总而言之,这种差异仍然可以归结为在执行Windows操作的操作期间CPU的活动。
我希望这个答案能够深入到您对StackOverflow的期望。