HomeSearch

C# String Switch Examples

Match string cases with a switch statement for improved performance. Test string literals.
String switch. Often we need to make a decision in code based on a string's value. We can test characters, or lengths, but sometimes we need to match entire strings.Switch
Switch notes. With a switch statement, the C# compiler implements hidden optimizations to speed up matching. This can help programs go faster.
An example. Here we use switch (on strings) to test whether a string is a moth name. Let's call our method that does this "IsMoth."

Tip: No fancy collections like a Dictionary or complex if-else chains are required here.

And: The C# compiler (our friend) will try to improve the switch's performance based on its internal heuristic.

IsMoth: Contains a switch case with 7 moth names in it. If any of those strings equal the parameter, it returns true. We have a moth name.

C# program that uses switch using System; class Program { static void Main() { Console.WriteLine(IsMoth("Ash Pug")); Console.WriteLine(IsMoth("Dot Net Perls")); } static bool IsMoth(string value) { switch (value) { case "Atlas Moth": case "Beet Armyworm": case "Indian Meal Moth": case "Ash Pug": case "Latticed Heath": case "Ribald Wave": case "The Streak": return true; default: return false; } } } Output True False
Internals. String switches are sometimes compiled into a custom Dictionary. Having 6 or more string cases and a default case resulted in the compiler generating a Dictionary.
Switch complexity 7 strings + default 6 strings + default 5 strings + default [and fewer] Compilation ldsfld class [mscorlib]System.Collections.Generic.Dictionary ldsfld class [mscorlib]System.Collections.Generic.Dictionary L_000b: call bool [mscorlib]System.String::op_Equality Output Compiler generated a Dictionary. Compiler generated a Dictionary. Compiler generated series if/conditionals.
ToLower. Switch cases must be constant. We cannot compare them case-insensitively without custom code. Here we normalize the string values with ToLower before entering the string switch.

Then: All the cases are lowercase. This results in a case-insensitive string switch.

Tip: The uppercase string "WHIPPET" was found to be a dog type. The value.ToLower() expression will match "whippet" in lowercase.

ToLower
C# program that uses switch, ToLower using System; class Program { static void Main() { Console.WriteLine(IsDogCaseInsensitive("WHIPPET")); Console.WriteLine(IsDogCaseInsensitive("sphynx")); } static bool IsDogCaseInsensitive(string value) { switch (value.ToLower()) { case "irish terrier": case "jagdterrier": case "keeshond": case "sulimov dog": case "whippet": case "eurasier": case "brittany": return true; default: return false; } } } Output True False
Null case. Most switch cases in C# a red on values (like 10 or 20) or string literals. But with the string switch we can match the null literal.

Note: In .NET Framework internals, null is like a 0 value, so it too is a constant—we call it the null literal constant.

C# program that uses null case in switch using System; class Program { static void Main() { foreach (string value in new string[] { null, "" }) { switch (value) { case null: // Can have a null case. Console.WriteLine("CASE NULL"); break; case "": // Empty string case also works. Console.WriteLine("CASE EMPTY"); break; } } } } Output CASE NULL CASE EMPTY
Abbreviations. A switch can handle abbreviations or synonyms. For example, in Excel spreadsheets, users will enter different column titles that mean the same thing.

So: We can use switches to tell the computer that "Height" is an abbreviation for "Ht."

Trim: We can use the Trim, TrimEnd, and TrimStart methods to try to normalize the input further.

TrimTrimEnd, TrimStart

Here: We handle abbreviations in a string switch, and also remove ending spaces. Further string logic could be added.

C# program that uses switch with abbreviations, Trim method using System; class Program { static bool IsHeight(string value) { // Match the trimmed value and return a bool. switch (value.Trim()) { case "Ht.": case "Height": return true; } return false; } static void Main() { Console.WriteLine("HEIGHT: {0}", IsHeight("Height")); Console.WriteLine("HEIGHT: {0}", IsHeight("Ht. ")); Console.WriteLine("HEIGHT: {0}", IsHeight("Width")); } } Output HEIGHT: True HEIGHT: True HEIGHT: False
Benchmark. Here we test a string switch against an if-else chain expression. The C# compiler turns the string switch into a Dictionary of strings. Then, cases perform a Dictionary lookup.

Version 1: We use the string switch to test the tree name strings. The switch is run in a tight loop.

Version 2: We use an expression, which is compiled into something like a series of if-else statements.

If

Result: The string switch statement is faster. If we cannot test for the most common string first, a string switch can improve performance.

C# program that benchmarks string switch using System; using System.Diagnostics; class Program { const int _max = 100000000; static void Main() { string[] trees = new string[] { "Adler", "Persimmon", "???" }; int treeCount = 0; var s1 = Stopwatch.StartNew(); // Version 1: use string switch. for (int i = 0; i < _max; i++) { foreach (string tree in trees) { if (IsTree(tree)) { treeCount++; } } } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use expression. for (int i = 0; i < _max; i++) { foreach (string tree in trees) { if (IsTreeExpression(tree)) { treeCount++; } } } s2.Stop(); Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) / _max).ToString("0.00 ns")); Console.Read(); } static bool IsTree(string value) { switch (value) { case "Alder": case "Elderberry": case "Chestnut": case "Guava": case "Willow": case "Elm": case "Persimmon": return true; default: return false; } } static bool IsTreeExpression(string value) { return (value == "Alder" || value == "Elderberry" || value == "Chestnut" || value == "Guava" || value == "Willow" || value == "Elm" || value == "Persimmon"); } } Output 32.94 ns switch (IsTree) 78.21 ns if (IsTreeExpression)
Result, above benchmark. In 2017, the switch version benchmarks faster. But here is an important point—in 2008 when I first tried this benchmark, the if-statement worked faster.

So: It may be useful to test the code on a system before you deploy it. Things might be wrong on the Internet.

Note: To achieve even better performance, you can try reordering the strings in an optimal way.

Tree: For complex situations where we have many strings and want to test them in a loop, consider a directed acyclic graph.

Tree
A summary. For performance, switches can be slower than if-statements. Often these small slowdowns do not matter. But when they do, an understanding of how switches work is important.
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to info@dotnetperls.com.
Home
Dot Net Perls