C# switch Examples

Branch on a constant in an efficient way with switch. Match value types, strings and patterns.

Switch. You find yourself in a mysterious dungeon. There is a dim light from above. The cell door is locked. You notice a box full of 1000 keys, all different.

Switch optimization. After trying 5 keys, you realize a faster way to switch keys is needed. As a developer, you know a switch statement has lookup table optimizations.

First example. This program uses a switch contained in the Transform method. With a switch, it tests the int against several constants: 0, 1 and 2.

Part 1: We always return a value from Transform(), so we do not need to have a return statement after the switch.

Part 2: We invoke Transform() with the arguments 0, 1 and 2—in the switch, we can see where these are matched.

C# program that uses switch using System; class Program { static int Transform(int argument) { // Part 1: use switch to return a value. switch (argument) { case 0: return 9; case 1: return 8; case 2: default: return 0; } } static void Main() { // Part 2: call Transform 3 times. Console.WriteLine(Transform(0)); Console.WriteLine(Transform(1)); Console.WriteLine(Transform(2)); } } Output 9 8 0

Curly brackets. This example introduces the default case, and a more verbose syntax. The program accepts an int from the user (with ReadLine). It then tests it for 6 values.Console.ReadLine

Here: We see how the curly brackets are used in the switch cases. And we combine some of the case statements.

C# program that uses int switch using System; class Program { static void Main() { while (true) { Console.WriteLine("Type number and press Return"); try { int i = int.Parse(Console.ReadLine()); switch (i) { case 0: case 1: case 2: { Console.WriteLine("Low number"); break; } case 3: case 4: case 5: { Console.WriteLine("Medium number"); break; } default: { Console.WriteLine("Other number"); break; } } } catch { } } } } Output Type number and press Return 5 Medium number Type number and press Return 2 Low number Type number and press Return 500 Other number

Strings. Often we switch on values, like numbers. The contents of strings are not value types—they are collections of characters. But with special support, we switch on strings too.String Switch

Note: The C# compiler detects a string switch and can optimize it with a Dictionary lookup.

Note 2: Small string switches, like this one with just 3 cases, are often not compiled into Dictionaries. Performance is better this way.

C# program that switches on string using System; class Program { static void Main() { string value = "turnip"; // ... Switch on the string. switch (value) { case "lettuce": Console.WriteLine("LETTUCE"); break; case "squash": Console.WriteLine("SQUASH"); break; case "turnip": Console.WriteLine("TURNIP"); break; } } } Output TURNIP

Goto, case default. We can use goto statements in switches. These are different from other gotos. With goto we can run multiple cases for a single expression.Goto

TestNameAndCode: This method switches on the code int, and then tests the name string. Goto and break are used to redirect control flow.

Case: We use this keyword to specify a matching constant. Default does not use this keyword. We end a case with break, return or continue.


Break: Break and continue can be used in any switch statement. These 2 keywords are used also within loops—this can be confusing.


Question: Are you breaking out of a switch, or out of the enclosing loop? Scope is important. The deepest construct is broken first.

C# program that uses case, goto, default in switch using System; class Program { static void TestNameAndCode(string name, int code) { switch (code) { case 200: case 300: case 400: if (name == "bird") { Console.WriteLine("Bird 200, 300, or 400"); break; } goto default; default: Console.WriteLine("Unknown"); break; } } static void Main() { // These will enter the if-statement. TestNameAndCode("bird", 200); TestNameAndCode("bird", 400); // This will go to the default case. TestNameAndCode("cat", 400); } } Output Bird 200, 300, or 400 Bird 200, 300, or 400 Unknown

Nested switch. Sometimes one switch is not enough. But we can nest a switch within another switch, successively testing values. This approach is sometimes helpful.Nested Switch

But: Our code logic, with nested switches, quickly turns into a mess. With comments, this approach may succeed.

Here: I test the first two elements in an int array with switches. The second element is tested if the first is 4.

C# program that uses nested switch using System; class Program { static void Main() { int[] array = { 4, 10, 14 }; switch (array[0]) { case 3: Console.WriteLine(3); // Not reached. break; case 4: Console.WriteLine(4); // ... Use nested switch. switch (array[1]) { case 10: Console.WriteLine(10); break; } break; } } } Output 4 10

Pattern matching, types. We can use pattern matching on types in a switch. We switch on a variable. In each case, we can match its type. A local variable (cast to that type) can be used.

Here: We introduce a class hierarchy—the Bird and Cat classes inherit from Animal. We then create some class instances.

And: We match the types of the Animal class. The most derived class is matched first—in this switch form, order matters.

C# program that uses switch, matches types using System; class Animal { public int size; } class Bird : Animal { public int color; } class Cat : Animal { public bool wild; } class Program { static void Test(Animal animal) { // Switch on a class type with pattern matching. switch (animal) { case Cat c: Console.WriteLine($"CAT wild = {c.wild}"); break; case Bird b: Console.WriteLine($"BIRD color = {b.color}"); break; case Animal a: Console.WriteLine($"ANIMAL size = {a.size}"); break; } } static void Main() { // Create some class instances. Cat cat = new Cat(); cat.wild = true; Bird bird = new Bird(); bird.color = 5; Animal animal = new Animal(); animal.size = 10; // Test class instances. Test(cat); Test(bird); Test(animal); } } Output CAT wild = True BIRD color = 5 ANIMAL size = 10

Pattern matching, when. We can place a condition on each case statement. This can test another variable. Here we test a local variable called secondValue on the first 2 cases.

Tip: We have repeat "case 200" statements. They are different only because of their "when" clauses.

Tip 2: With the "when pattern-matching" syntax, order matters in a switch. This is an enhanced syntax form.

C# program that uses pattern matching, when keyword using System; class Program { static void Main() { int value = 200; int secondValue = 300; // Use switch with pattern matching. switch (value) { case 200 when secondValue == 0: Console.WriteLine("Y"); break; case 200 when secondValue == 300: Console.WriteLine("Value is 200, secondValue is 300"); break; case 400: Console.WriteLine("Z"); break; } } } Output Value is 200, secondValue is 300

Double. Some values may not be switched upon. Floating-point values, like doubles, will cause an error in a switch expression. Most classes and custom types will also fail.

Bool: A bool may be used in the expression of a switch. But this is somewhat strange as only true and false are allowed.

Nullable: A nullable type can be used, but only if the nullable type "wraps" a valid switch type like an enum.

C# program that causes switch double error class Program { static void Main() { double value = 1.4; switch (value) { case 1: break; } } } Output Error 1 A switch expression or case label must be a bool, char, string, integral, enum, or corresponding nullable type....

Fall through. Every case must have a break, continue, goto, return or throw at its end. In C# we cannot have cases with statements fall through to the following case.

Goto: We can use the goto statement, as in "goto case 1," to run both cases on a 0 value. As shown, the program does not compile.

C# program that has fall-through error using System; class Program { static void Main() { int value = 0; // ... Every switch statement must be terminated. switch (value) { case 0: Console.WriteLine("Zero"); case 1: Console.WriteLine("One"); break; } } } Output Error 1 Control cannot fall through from one case label ('case 0:') to another

Duplicate cases. A switch can only have unique case labels—each constant value must be distinct. This program will not compile. But it shows us what happens when we have duplicate cases.
C# program that has duplicate case using System; class Program { static void Main() { short number = 0; // ... Cases may not be duplicated. switch (number) { case 0: case 0: Console.WriteLine("ZERO"); return; case 1: Console.WriteLine("ONE"); return; } } } Output Error 1 The label 'case 0:' already occurs in this switch statement

Constants. We must use only constants for case statements. This is a limitation, but it is part of the language specification. The C# compiler is not even tempted.
C# program that lacks constant case using System; class Program { static void Main() { int number = 0; int test = 10; // ... Constant values are required. switch (number) { case test + 1: Console.WriteLine(100); return; case 0: Console.WriteLine(0); return; } } } Output Error 1 A constant value is expected

Benchmark. A switch statement helps optimize some programs. Suppose a method that must return a value based on a small range of inputs. We can use switch to test these values.

Version 1: This version of the code uses a switch statement. It returns an int based on the argument int "v."

Version 2: This code does the same thing as Method 1, but instead uses a series of if-statements.

Result: The benchmark shows that the switch statement version is slightly faster. Those nanoseconds may come in handy someday.

C# program that benchmarks switch using System; using System.Diagnostics; class Program { static int Method1(int v) { switch (v) { case 0: return 10; case 1: return -1; case 2: return 20; default: return 0; } } static int Method2(int v) { if (v == 0) return 10; if (v == 1) return -1; if (v == 2) return 20; return 0; } static void Main() { Method1(0); Method2(0); const int max = 100000000; var s1 = Stopwatch.StartNew(); // Version 1: use switch. for (int i = 0; i < max; i++) { Method1(0); Method1(1); Method1(2); Method1(3); } s1.Stop(); var s2 = Stopwatch.StartNew(); // Version 2: use if-else. for (int i = 0; i < max; i++) { Method2(0); Method2(1); Method2(2); Method2(3); } 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(); } } Output 9.25 ns [switch] 9.85 ns [if]

Types. It is possible to switch on integers or other value types, such as enums or chars. Strings are a reference type, but the C# compiler can handle switches on strings as well.Switch CharSwitch Enum

Speed. Switch can be implemented with a jump table in the intermediate language. Large switches can be much faster than long series of if-else statements.

1. If and switch. The if-statement sometimes performs better than the switch. Testing is essential.

If, Switch Comparison

2. Regex and switch. The switch statement can be used to replace a Regex method call. This can make programs much faster.

Regex, Switch

3. Intermediate language: Switch is often implemented at a lower level with the switch opcode. We show an example.

IL: switch

Usage. Should we use switches everywhere? This is probably a bad idea. With polymorphism, we abstract selections with virtual methods and inheritance. This leads to cleaner code.Virtual

Some thoughts. Switches often outperform if-statements. But more importantly, they help with code design. They enforce all values tested are constants.

A summary. This construct imparts a greater sense of symmetry. Switches test value types and strings. They speed up selections. And with them, we write clearer code.
Dot Net Perls
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to