Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the order of checking whether a string is null or empty matter?

Tags:

c#

For my programming exam I had to defend code I had written. One of the lines are:

if(app.Logourl == "" || app.Logourl == null)

He asked me whether there was a difference between null and an empty string. I told him that the difference was that null means its not pointing to anything, so it's not instantiated, but the empty string is.

After the exam I walked up to him and asked him if I was correct, since I saw a funny look on his face. He told me that it's true they're different, but the order in which I checked the values was incorrect.

Now a few days later I believe there's nothing wrong with the order. Am I correct?

TL;DR

is

if(app.Logourl == "" || app.Logourl == null)

equivalent to

if(app.Logourl == null || app.Logourl == "")
like image 388
user1534664 Avatar asked Jan 31 '15 10:01

user1534664


2 Answers

It's OK the way you did it, because the overload of == for System.String calls String.Equals, which allows nulls.

This is not universal, however: if you wanted to check string length instead of using == "", your first code snippet would be in trouble:

if(app.Logourl.Length == 0 || app.Logourl == null) // <<== Wrong!

while the second one would be fine:

if(app.Logourl == null || app.Logourl.Length == 0) // <<== Correct

The reason for this is short circuiting in the evaluation of || and && operators: once they know the result (true for ||, false for &&) they stop evaluation. In the second snippet above, if app.Logourl is null, the second half of the expression will be ignored, hence app.Logourl.Length would not throw a null reference exception.

Note: In recognition of checks like this happening all over the place, C# class library offers a convenience method for doing this check:

if (string.IsNullOrEmpty(app.Logourl)) {
    ...
}
like image 172
Sergey Kalinichenko Avatar answered Oct 21 '22 02:10

Sergey Kalinichenko


    private static bool IsNullOrEmpty(string s)
    {
        return s == null || s == "";

        /*
         Lets look behind the scenes here:
         =================================
        IL_0000: ldarg.0   => load s on the evaluation stack   
        IL_0001: brfalse.s IL_000f => GoTo label 'IL_000f' if loaded argument is null

        IL_0003: ldarg.0    => load s on the evaluation stack  
        IL_0004: ldstr ""   => load constant string "" to the evaluation stack
        IL_0009: call bool [mscorlib]System.String::op_Equality(string, string)
                              => Call String.Equality(string,string) with s and "" 
                                 loaded to the evalutation stack
                                 that will pop the two values compare them for equality and load the result.
                                 to the evaluation stack.                                     

        IL_000e: ret  => Return to the caller with equlity result on the evauation stack.

        IL_000f: ldc.i4.1 => Load constant value 1(4 byte which will represent "True") to the evaluation stack
                             and return to the caller.In our flow it's the case when s is null.
        IL_0010: ret 

         In Summary:
         ===========
        1.) IL instructions total code size 17 bytes.
        2.) Best case scenario execution path => 2 IL instructions.
        3.) Worst case scenario execution pat => 8 IL instructions.

        */
    }

    private static bool IsEmptyOrNull(string s)
    {
        return s == "" || s == null;

        /*
         Lets look behind the scenes here:
         =================================
         IL_0000: ldarg.0  => load s on the evaluation stack   
         IL_0001: ldstr "" => load constant string "" to the evaluation stack
         IL_0006: call bool [mscorlib]System.String::op_Equality(string, string)
         IL_000b: brtrue.s IL_0012

         IL_000d: ldarg.0 => load s on the evaluation stack   
         IL_000e: ldnull  => load constant null on the evaluation stack 
         IL_000f: ceq => Pop two loaded values compare and push the result back on the evaluation stack
         IL_0011: ret

         IL_0012: ldc.i4.1 => Load constant value 1(4 byte which will represent "True") to the evaluation stack
                             and return to the caller.In our flow it's the case when s is null.
         IL_0013: ret 

          In Summary:
         ===========
        1.) IL instructions total code size 20 bytes.
        2.) Best case scenario execution path => 6 IL instructions.
        3.) Worst case scenario execution path => 10 IL instructions.
     */

    }

Conclusion:

Judging only by the IL emitted code "if(app.Logourl == "" || app.Logourl == null)" is "microptimization" better performance wise :)

like image 43
Roman Ambinder Avatar answered Oct 21 '22 03:10

Roman Ambinder