-1

Code Sample :

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
}

Question 1: Is there any difference between region 1 and 2 at C# ?:

Question 2: Does all the compilers/interpreters behave same at this situation?

Note: I know there is something int.tryparse(). I just want to understand how compiler is working at this situation.


Note 2: Someone stuck compiler must give an error, let me explain then:

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;
    }

}

Compiler gives no error. So we bypassed compiler. And if we execute code :

Test();

b is 99

Fill_A();
Test();

b is 100.

Integers are not our focus at this problem. So i hope now we can focus to the questions (:

  • 5
    It is impossible for an `int` (which is a `struct`) to be null. – myermian Jul 16 '14 at 22:04
  • You could try compiling the code and comparing the IL produced. – Andrew Cooper Jul 16 '14 at 22:05
  • @AndrewCooper Or rather, he could just *try compiling the code* (it won't). – Michael Jul 16 '14 at 22:06
  • Random code note, your first condition says "a>0" when you probably meant "var_a>0". If you did, you may want to fix it. – BradleyDotNET Jul 16 '14 at 22:08
  • @Michael - Agreed, but I'm assuming that in writing code that the compiler will accept the OP will fix the glaring errors. – Andrew Cooper Jul 16 '14 at 22:16
  • To test such things yourself, you should try [LINQPad](http://linqpad.net), it can show the generated IL for your program. Simply create two methods, each with their own "region" set of if-statements, and inspect the generated IL. – Lasse V. Karlsen Jul 16 '14 at 22:19
  • @BradleyDotNet I cant try to compile it now. I am writing in mono at linux and i 'll carry and compile my code tomorrow by a Microsoft compiler. That's why i am asking this. –  Jul 16 '14 at 22:29
  • The IL removes the null check because int can't be null. – ProgrammingLlama Jul 16 '14 at 22:37
  • @john it is accepted as null if nothing is assigned. –  Jul 16 '14 at 22:38
  • 1
    @MansonHanson In Visual Studio, at least, it won't compile if nothing is assigned. I had to assign a value to it so that it would compile. Upon viewing it through JustDecompile, I found that the null check had been removed altogether (obviously). You may want to look into how Value Types work: http://msdn.microsoft.com/en-gb/library/s1ax56ch.aspx – ProgrammingLlama Jul 16 '14 at 22:40
  • @john i am using the first statement and "if block" doesn't executed when var_a is not assigned and executed when var_a is assigned and bigger than 0 and throws no error neither in compile time nor in execution time. U may try it yourself. –  Jul 16 '14 at 22:43
  • @MansonHanson Done: http://s16.postimg.org/h0js65r3p/test.png – ProgrammingLlama Jul 16 '14 at 22:46
  • 1
    @MansonHanson The MSDN documentation I have previously linked to also clearly states that you cannot use an int before you assign a value to it. Perhaps you might want to try this yourself? I suspect you may have an = rather than an == somewhere if your code is working and compiling. – ProgrammingLlama Jul 16 '14 at 22:47
  • @john Maybe u may read my edit. There is an example compile usage of an int without assigning it –  Jul 17 '14 at 05:48
  • 1
    Kinda half-right. Fields of classes are initialized automatically with their default value (0 in this case). It is assigned before the value is observable. http://stackoverflow.com/a/268417/1974021 – DasKrümelmonster Jul 17 '14 at 06:20
  • As DasKrumelmonster said, in this case it defaults to 0. Again, impossible to be null (what part of this do you not understand?) – ProgrammingLlama Jul 17 '14 at 10:23
  • When i debug the code "if" fails in this.a != null part not in a > 0 part. Your answer doesn't explain this. That part i don't understand. Anyway. As u can see the focus is the question is not integer could be null or couldn't be. Why did u stick on this? May we focus on the **"Is there any difference between region 1 and 2 at C# ?:"** part now please? –  Jul 17 '14 at 11:04
  • @MansonHanson, you may want to consider just editing to use a reference type so `null` is a valid value for the variable. Regardless, we should definitely focus on the difference part :) – BradleyDotNET Jul 17 '14 at 15:05
  • There is no difference, happy? – ProgrammingLlama Jul 17 '14 at 17:24
  • Furthermore, you could see this yourself by compiling your program and then looking at how something like Telerik's JustDecompile shows it as the code seen in JustDecompile is C# code generated from the compiled IL code. – ProgrammingLlama Jul 17 '14 at 18:12
  • John in other words you can easily say you dont know anything about the problem.It is OK.But let people focus on the question.Not your very important int.. –  Jul 17 '14 at 20:12
  • As pointed out by others, this is a site for collaboration. The answers given regarding ints to pertain to your question as we were simply pointing out that doing the null check is completely irrelevant as it cannot be null. Therefore to 'optimize' your code, you can remove it altogether and only worry about the value of var_A being > 0. To remind you of your questions, you wanted to know the difference between each code block and if all compilers behave the same. In my first comment I told you they compiled down to the same thing. Stop being so argumentative and accept the help people provide – ProgrammingLlama Jul 17 '14 at 23:04

2 Answers2

7

Question 1: No, there is no functional difference. The IL could be different, but it will be minor if anything (and the IL produced could change in the next spec/compiler)

Question 2: Not having seen every C# compiler or interpreter's source, its probably impossible to know. Again, a valid compiler/interpreter will produce functionally the same code.

Note that your example is non-sense, an int is never null. Regardless, nesting as opposed to && will provide the same behavior.

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
  • It seems the C# compiler produces identical output from the two. Tested two methods each with one "region" set of if-statements, and the IL is identical between the two in [LINQPad](http://linqpad.net). This is an *observation* however, I don't know any part of the C# specification guarantees this behaviour or if it is simply an implementation detail. – Lasse V. Karlsen Jul 16 '14 at 22:18
  • If an int is not assigned Microsoft's C# compiler accepts it is null or behaves like that. It doesn't throws any exception and behaves like null in "if" check –  Jul 16 '14 at 22:26
  • 2
    @MansonHanson Just tried it, and it failed to build as "unassigned variable used". If you *do* assign it, you get a warning saying the expression is always true, since `int` can never be equal to null. Tried in VS 2012 – BradleyDotNET Jul 16 '14 at 22:47
  • It's no use arguing with him Bradley, I've even given him a link to the MSDN page on value types which states you can't use an int type without initializing it. He's still happy to swear that black is white.... – ProgrammingLlama Jul 16 '14 at 22:50
  • 1
    @john, saw your "discussion" after I tried/posted. Definitely seems like he is either trying bad code, or just not listening. – BradleyDotNET Jul 16 '14 at 22:51
  • @MansonHanson Yes, a class initializer will go to 0 (*not* null) but as you say, thats not the focus of the question (hence my inclusion as a "note", you could have easily used a reference type in the example and been fine). Do you have any questions about my answer? With all the discussion about the null check, I've only had the one comment about it, and I want to make sure your question gets answered. – BradleyDotNET Jul 17 '14 at 15:04
  • @BradleyDotNET i am waiting for other answers, because of damn INT, the discussions are only around very big "int can or cannot be null" problem. I am waiting for other answers becuse i am not satisfied. Let me explain why: U said there is no functional difference yes it is true.I can also see this. But i know there is operational differences when u think this at ALU level. I just want to know how compiler handles this. For example in python there is no null objects. Python creates a memory area for an empty object when u set variable=None. But how .Net compiler does this? –  Jul 17 '14 at 20:10
  • 1
    @MansonHanson You are being extremely argumentative on a site that's meant for collaboration and complaining about not receiving an answer to a question you haven't asked. *We are not mind readers.* If I were you, I would delete this question while you still can and take some time to formulate your thoughts before you post another. – Michael Jul 17 '14 at 20:30
  • @MansonHanson First off, if you are trying to optimise at the ALU level, C# is **not** the right language for you. All you need to know as far as differences is what IL (intermediate language) they compile down to. It sounds like they are the same (at least in two compilers). Without some input from Eric Lippert, I won't be able to say if they are *required* to (I doubt it though). The point is, for all intents and purposes they are the same. Any difference in compiled code will be negligible. If you are truly worried about performance, you need to benchmark it yourself. – BradleyDotNET Jul 17 '14 at 20:56
2

Ok, to answer your question the way you want it because you're being difficult. If you want a different answer, I highly suggest that you actually ask the question you want answered.

I am compiling this code, first with only region 1 and then second with only region 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;
}

If I extract the IL code for region 1, I get this:

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

And for region 2, I get this:

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

So, yes, there is a slightly difference. JetBrains DotPeek shows a difference.

Region 1:

if (num > 0)
    num = -1;

Region 2:

int num = 0;
bool flag = 1 == 0;
if (num > 0)
    num = -1;

Whereas JustDecompile cleans things up a bit and shows the same IL->C# conversion for both:

if (var_a > 0)
{
    var_a = -1;
}

Since you care so much about efficiency, I've written a quick bit of code to try and benchmark the difference:

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();
}

Values for A:

2918.6503
2910.8609
2916.2404
2909.5394
2914.0309
2961.0775
2948.4139
2957.1939
2962.1737

Values for B:

3170.8064
2891.6971
2924.8533
2890.6248
2885.1991
2890.6321
2887.0145
2935.6778
2909.035

The average for the entire 100,000,000 execution cycle for A vs B is 2933 ms vs 2932 ms respectively.

Per execution of the internal block, that's 2.9331 * 10^-5 vs 2.9317 * 10^-5.

Now we've got down to this, I have to ask why you would be writing a program in as high a level language as C# when you care about something that makes 0.000000014045333333 ms difference between one way and the other. Perhaps you should try something more low level like Assembly? All in all, this discrepancy could still come down to activity of the CPU during that operation doing other things for Windows.

I hope this answer goes into the depth you've come to expect from StackOverflow.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86
  • I dont care about the performance. Generally i prefer java or python for high level jobs. But i have to use c# at the company that i work for. So i am trying to understand how C# compiler works. I am a linux user except work so I cant decompile/compile to IL to investigate everytime i want (: –  Jul 18 '14 at 06:30
  • I found something that the IL u pasted. At the first part. The bytecode u pasted first compares the zero part, then compares the null part. Do u know why? By the way i cant give +1 vote to u. I need 15 reputation. –  Jul 18 '14 at 06:40
  • I'm not sure I'm afraid – ProgrammingLlama Jul 18 '14 at 20:00
  • +1 for the penultimate paragraph alone. @MansonHanson not trying to be argumentative, but you really should think about *why* this matters to you. The point of using a high-level language like C# is that we don't need to worry about stuff like this. Code readability is worth way more than micro-optimizations like this. – BradleyDotNET Jul 19 '14 at 05:13
  • @BradleyDotNET I am a computer engineer nearly 8 years, coming from C culture, an active python coder, and please let me choose what i matter or not. Thanks for your answer, but i think u must stop to be argumentative at this point. Because i DIDN'T said anything about performance. U are carrying the problem always far away. First int , now performance....please... –  Jul 19 '14 at 23:54
  • @John thanks for your effort.I think i am going to continue to wait.. :) –  Jul 19 '14 at 23:55
  • @MansonHanson Clearly we aren't going to see eye-to-eye on this. I hope you get the answer you are looking for. – BradleyDotNET Jul 20 '14 at 00:31