检查if语句中的null行为

本文关键字:null 行为 if 语句 检查 | 更新日期: 2023-09-27 18:13:25

代码示例:

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.trearse((中有一些东西。我只想了解编译器在这种情况下是如何工作的。


注意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。

整数不是我们关注的问题。所以我希望现在我们可以专注于问题(:

检查if语句中的null行为

问题1:不,没有功能差异。IL可能不同,但如果有任何变化,它将是次要的(并且生成的IL可能在下一个规范/编译器中发生变化(

问题2:没有看过每个C#编译器或解释器的源代码,可能不可能知道。同样,一个有效的编译器/解释器将在功能上产生相同的代码。

请注意,您的示例是无意义的,int从不为空。无论如何,嵌套与&将提供相同的行为。

好吧,按照你想要的方式回答你的问题,因为你很难回答。如果你想要一个不同的答案,我强烈建议你真的问你想要回答的问题。

我正在编译这个代码,首先只使用区域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的整个100000000执行周期的平均值分别为2933 ms2932 ms

每次执行内部块,这是2.9331 * 10^-52.9317 * 10^-5

现在我们开始讨论这个问题,我不得不问,当你关心0.000000014045333333 ms在某种方式和另一种方式之间的差异时,为什么你会用C#这样的高级语言编写程序。也许你应该尝试一些更低级的东西,比如Assembly?总而言之,这种差异仍然可以归结为CPU在为Windows做其他事情的操作过程中的活动。

我希望这个答案能深入到您对StackOverflow的期望。