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 == "")
It's OK the way you did it, because the overload of ==
for System.String
calls String.Equals
, which allows null
s.
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)) {
...
}
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.
*/
}
Judging only by the IL emitted code "if(app.Logourl == "" || app.Logourl == null)" is "microptimization" better performance wise :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With