tags:

views:

631

answers:

3

If I have a switch-case statement where the object in the switch is string, is it possible to do anyway ignoreCase compare?

I have for instance:

string s = "house";
switch (s)
{
  case "houSe": s = "window";
}

Will s get value "window". How to override the switch-case statement so it will compare the strings using ignoreCase?

+20  A: 

A simpler approach is just lowercasing your string before it goes into the switch statement, and have the cases lower.

Actually, upper is a bit better from a pure extreme nanosecond performance standpoint, but less natural to look at.

E.g.:

string s = "house"; 
switch (s.ToLower()) { 
  case "house": 
    s = "window"; 
    break;
}
Nick Craver
@Nick, do you have any reference to the reason for the performance difference between the lower and upper conversions? Not challenging it just curious.
Lazarus
Yes, I understand that lowercasing is a way, but i want from it to be ignoreCase. Is there a way that i can override the switch-case statement?
Tolsan
@Lazarus - This is from CLR via C#, it was posted here a while back in the hidden features thread as well: http://stackoverflow.com/questions/9033/hidden-features-of-c/12137#12137 You can fire up LinqPad with a few million iterations, holds true.
Nick Craver
@Tolsan - No, unfortunately there's not just because of it's static nature. There was a good batch of answers on this a while back: http://stackoverflow.com/questions/44905/c-switch-statement-limitations-why
Nick Craver
A: 

Yes, I understand that lowercasing is a way, but i want from it to be ignoreCase. Is there a way that i can override the switch-case statement?

I don't think you can "override" the switch statement handling. And even if you could, it isn't advisable... the generally accepted solution to this problem is to upper or lowercase the string before passing it to switch, as Nick shows in his answer.

Why would you ignore the commonly accepted practice in favor of changing the way a low-level, fundamental piece of the language works?

Seth Petry-Johnson
No need to reinvent the wheel!
Frank
Changing the switch statement behavior isn't an option, but in this case the commonly accepted practice is also wrong.
Jeffrey L Whitledge
+8  A: 

As you seem to be aware, lowercasing two strings and comparing them is not the same as doing an ignore-case comparison. There are lots of language-specific reasons for this.

Unfortunately, switch doesn't do anything but an ordinal comparison.

What I have done in the past to get the correct behavior is just mock up my own switch statement. There are lots of ways to do this. One way would be to create a List<T> of pairs of case strings and delegates and just loop through the list doing to proper comparison, and then invoke the delegate that matches.

The great thing about this is that there isn't really any performance penalty in mocking up your own switch functionality when comparing against strings. The system isn't going to make a O(1) jump table the way it can with integers, so it's going to be comparing each string one at a time anyway.

Something like this:

delegate void CustomSwitchDestination();
List<KeyValuePair<string, CustomSwitchDestination>> customSwitchList;
CustomSwitchDestination defaultSwitchDestination = new CustomSwitchDestination(NoMatchFound);
void CustomSwitch(string value)
{
    foreach (var switchOption in customSwitchList)
        if (switchOption.Key.Equals(value, StringComparison.InvariantCultureIgnoreCase))
        {
            switchOption.Value.Invoke();
            return;
        }
    defaultSwitchDestination.Invoke();
}

Of course, you will probably want to add some standard parameters a return type to the CustomSwitchDestination delegate, and you'll want to make better names!

If your behavior of each of your cases is not amenable to delegate invocation in this manner, such as if differnt parameters are necessary, then your stuck with chained if statments. I've also done this a couple of times.

    if (s.Equals("house", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "window";
    }
    else if (s.Equals("business", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "really big window";
    }
    else if (s.Equals("school", StringComparison.InvariantCultureIgnoreCase))
    {
        s = "broken window";
    }
Jeffrey L Whitledge
Unless I'm mistaken, the two are only different for certain cultures (like Turkish), and in that case couldn't he use `ToUpperInvariant()` or `ToLowerInvariant()`? Also, he's not comparing _two unknown strings_, he's comparing one unknown string to one known string. Thus, as long as he knows how to hardcode the suitable upper or lower case representation then the switch block should work fine.
Seth Petry-Johnson
@Seth Petry-Johnson - Perhaps that optimization could be made, but the reason the string comparison options are baked into the framework is so we don't all have to become linguistics experts to write correct, extensible software.
Jeffrey L Whitledge
OK. I'll give an example where this is relivant. Suppose instead of "house" we had the (English!) word "café". This value could be represented equally well (and equally likely) by either "caf\u00E9" or "cafe\u0301". Ordinal equality (as in a switch statement) with either `ToLower()` or `ToLowerInvariant()` will return false. `Equals` with `StringComparison.InvariantCultureIgnoreCase` will return true. Since both sequences look identical when displayed, the `ToLower()` version is a nasty bug to track down. This is why it's always best to do proper string comparisons, even if you're not Turkish.
Jeffrey L Whitledge