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.

14 comments:

  1. 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.

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

    someString.EqualsIgnoreCase("somevalue")

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

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

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

    ReplyDelete
  6. 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.

    ReplyDelete
  7. 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

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

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

    ReplyDelete
  10. I prefer Sring.Compare()

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

    ReplyDelete
  11. 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

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

    ReplyDelete
  13. lifescience neighbors bound restrained researchers paragraphs nasdaq friesen keydocuments least tourist
    lolikneri havaqatsu

    ReplyDelete
  14. It was very nice blog, thanks for shearing this idea.

    ReplyDelete