Friday, February 01, 2008

When string.ToLower() is Evil


Did you know how evil string.ToLower() can sometimes be?

Let me explain...

Very often I see code similar to this:

void DoBadAction (string val)
{
if (val.ToLower() == "someValue")
{ //do something
}
}

The code above can lead up to 4 times in performance loss when doing string comparison operations.

Best method to do such kind of case insensitive comparison is using string.Equals(...) method.

void DoGoodAction(string val)
{
if (val.Equals("someValue", StringComparison.OrdinalIgnoreCase))
{ //do something
}
}

Why is it so? The reason lies in the string type peculiarity - it is immutable.

Since it is an immutable - string.ToLower() will always return new string instance. Thus generating extra instance of string on every ToLower() call.

Detailed information about string.Equals with StringComparison enumeration can be found here.
Other performance related tips and tricks can be found here.

12 коментарі:

dotstop said...

Since you're talking performance, how about showing some stats. When will the performance gain matter ? ..... etc ..

Since you're stating a 4 times improvement , you should also provide some code.

Chad Myers said...

Sounds like a good excuse for an Extension Method in C# 3.0:

someString.EqualsIgnoreCase("somevalue")

Anonymous said...

if (val.ToLower() == "someValue")
{ //this code will never be reached
}

ignu said...

you've got a slightly bigger problem then performance, in that your if statement will never return true

Anonymous said...

Thanks for the tip. I've been using .tolower() for a long time now.

Vadym Stetsiak said...

if (val.ToLower() == "someValue")
{ //this code will never be reached
}

That code was somewhat ironical.
Nor it was a sample of bad comparison, but also source of potential bugs.

Vadym Stetsiak said...

Here are the timings received when doing comparison:

Time with .ToLower(): 00:00:00.5100357, With .Equals: 00:00:00.1893425. N times: 2,69
Time with .ToLower(): 00:00:00.5379754, With .Equals: 00:00:00.1816390. N times: 2,96
Time with .ToLower(): 00:00:00.4981258, With .Equals: 00:00:00.1801120. N times: 2,77
Time with .ToLower(): 00:00:00.5016140, With .Equals: 00:00:00.1814274. N times: 2,76
Time with .ToLower(): 00:00:00.5302074, With .Equals: 00:00:00.1734722. N times: 3,06
Time with .ToLower(): 00:00:00.5109213, With .Equals: 00:00:00.1852686. N times: 2,76
Time with .ToLower(): 00:00:00.5015992, With .Equals: 00:00:00.1766858. N times: 2,84
Time with .ToLower(): 00:00:00.5228205, With .Equals: 00:00:00.1793420. N times: 2,92
Time with .ToLower(): 00:00:00.5160138, With .Equals: 00:00:00.1861520. N times: 2,77
Time with .ToLower(): 00:00:00.5028534, With .Equals: 00:00:00.1801821. N times: 2,79
Time with .ToLower(): 00:00:00.5099061, With .Equals: 00:00:00.1817483. N times: 2,81
Time with .ToLower(): 00:00:00.5155395, With .Equals: 00:00:00.1851748. N times: 2,78
Time with .ToLower(): 00:00:00.4984701, With .Equals: 00:00:00.1841835. N times: 2,71
Time with .ToLower(): 00:00:00.5664112, With .Equals: 00:00:00.1917371. N times: 2,95
Time with .ToLower(): 00:00:00.5351288, With .Equals: 00:00:00.1791308. N times: 2,99
Time with .ToLower(): 00:00:00.5135746, With .Equals: 00:00:00.1900286. N times: 2,70
Time with .ToLower(): 00:00:00.5044958, With .Equals: 00:00:00.1747117. N times: 2,89
Time with .ToLower(): 00:00:00.5240497, With .Equals: 00:00:00.1732485. N times: 3,02
Time with .ToLower(): 00:00:00.5106127, With .Equals: 00:00:00.1876307. N times: 2,72
Time with .ToLower(): 00:00:00.4953805, With .Equals: 00:00:00.1818622. N times: 2,72

Ferry said...

Thanks to Vadym for the stats. I suppose it's kinda clear from the performance side, Equals is better. And it's neat too =)

Anonymous said...

Alas, we're still left with ToLower() or ToUpper() when using CompareTo() :(

Anonymous said...

I prefer Sring.Compare()

Ex:
String.Compare (string strA, string strB, bool ignoreCase);

DotNetGuts said...

This Post will definately explain you how you can overcome the problem of comparision

http://dotnetguts.blogspot.com/2007/07/improving-performance-of-net.html

Vadym Stetsiak said...

Good article.
StringBuilder is not always necessary, see this artcile http://www.yoda.arachsys.com/csharp/stringbuilder.html