Following on from Jeff Atwood's
post
about comparing a string to empty string (""), I found out that C# and
VB.net do not behave the same when comparing a string that is null.
In the following C# snippet, the code will display
The String is NOT Null:
string s = null;
if (s == "")
Console.WriteLine("The String IS Null");
else
Console.WriteLine("The String is NOT Null");
The following VB.net code will display
The String IS Null:
Dim s as string = Nothing
If s = "" Then
Console.WriteLine("The String IS Null")
else
Console.WriteLine("The String is NOT Null")
End If
The IL for the C# code is:
.method private hidebysig static void
Main(string[] args) cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
= ( 01 00 00 00 )
// Code size 38 (0x26)
.maxstack 2
.locals init (string V_0)
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: ldstr ""
IL_0008:
call bool
[mscorlib]System.String::op_Equality(string,string)
IL_000d: brfalse.s IL_001b
IL_000f: ldstr "The String IS Null"
IL_0014:
call void
[mscorlib]System.Console::WriteLine(string)
IL_0019: br.s IL_0025
IL_001b: ldstr "The String is NOT Null"
IL_0020:
call void
[mscorlib]System.Console::WriteLine(string)
IL_0025: ret
} // end of method Class1::Main
The IL for the VB.net code is:
.method public static void Main() cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() =
( 01 00 00 00 )
// Code size 46 (0x2e)
.maxstack 3
.locals init ([0] string s)
IL_0000: nop
IL_0001: ldnull
IL_0002: stloc.0
IL_0003: ldloc.0
IL_0004: ldstr ""
IL_0009: ldc.i4.0
IL_000a:
call int32
[Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.
StringType::StrCmp(string,string,bool)
IL_000f: ldc.i4.0
IL_0010: bne.un.s IL_001f
IL_0012: ldstr "The String IS Null"
IL_0017:
call void
[mscorlib]System.Console::WriteLine(string)
IL_001c: nop
IL_001d: br.s IL_002b
IL_001f: nop
IL_0020: ldstr "The String is NOT Null"
IL_0025:
call void
[mscorlib]System.Console::WriteLine(string)
IL_002a: nop
IL_002b: nop
IL_002c: nop
IL_002d: ret
} // end of method Module1::Main
As we can see from the VB.net IL, the string comparison is replaced
with a call to
Microsoft.VisualBasic.CompilerServices.StringType::StrCmp. From further
reading I understand that Microsoft did this to maintain compatibility
with VB6. The C# compiler on the other hand uses op_Equality to check
the equality of empty string and null. Since null and empty string are
not equal C# returns false from the condition.
So, the debate about whether to use
S == ""
or not just about performance, but also about expected operational
behavior. Developers who switch between C# and VB.net could find some
unexpected results in their software if they are not aware of this
idiosyncrasy.
Going back to performance.... Chris Taylor has some nice graphs on
his
blog, which show the significant differences in time between
S1 == S2,
S1.op_Equality(S1),
S1.Equals(S1) and
String.Equals(S1, S2). Performance varied by test but
S1.op_Equality(S1) seemed to be an overall god performer, which is real handy because it's what the C# compiler chooses when optimizing
S1 == S2.
As much as I hate to see code, which doesn't read as one would expect, VB.net programmers might want to consider using
String.Equals(S1, S2) or
S1.Length > 0 when doing lots of string comparisons.
In both C# and VB.net worlds, it's good development practice to check
your strings against null before performing equality operations. Never
assume the compiler is always going to do the work for you - oh it
brings me back to my C++ days when the compiler did crap for you.