Strange case of CS0029 (type mismatch) with switch statement

Viewed 75

Following code fails with the CS0029 error. Indeed this code is wrong - you should either start with tuple and have cases for tuples, or start with single variable and match on strings. The question is what compiler actually tries to do and why it accepts switch (s1,s2) as valid part of switch statement.

CS0029 Cannot implicitly convert type 'string' to '(string s1, string s2)'

string s1 = "a", s2 = "b";

switch (s1,s2)
{
    case "a": //CS0029 
        Console.WriteLine("A"); 
        break; 
    default: break;
}

Variants that I expect to work (meaning it compiles and code in case runs):

tuple (braces around the tuple for the switch's syntax):

switch ((s1,s2)) {
   case ("a","b"):...

or single variable:

switch(s1) {
   case "a":

And one that I don't really expect to work but it does (which is what error message suggests essentially) - this case compiles and at run-time case code is executed (same way as switch ((s1,s2))):

switch (s1,s2) {
   case ("a","b"):...

Based on behavior and the error it looks like switch (s1,s2) is treated as switch ((s1,s2)) but it is unclear why based on docs for the switch statement'

switch_statement : 'switch' '(' expression ')' switch_block ;


Inspired by currently deleted https://stackoverflow.com/questions/71803871

1 Answers

Your code here:

switch (s1,s2) {
   case ("a","b"):...

is compiled into this:

private static void Main(string[] args)
{
    (string, string) tuple2 = ("a", "b");
    if (tuple2 == ("a", "b"))
    {
        Console.WriteLine("A");
    }
}

The reason for this is the native switch instruction of CIL switch <uint32, int32, int32 (t1..tN)>. In the creation of a jump table a pair of values (s1,s2) must be considered a single value N if both must equal a specified value for the case to be the target jump. This means the compiler interprets this as a tuple because the jump table cannot work otherwise.

EDIT:

You actually get some interesting results if you mess with it a bit. For instance

        static void Main(string[] args)
        {
            string s1 = "a";
            string s2 = "b";
            string s3 = "a";
            string s4 = "b";
            string s5 = "a";
            string s6 = "b";
            string s7 = "a";
            string s8 = "b";
            string s9 = "a";
            string s10 = "b";

            switch ((s1,s2),(s3,s4),s5,s6,s7,s8,s9,s10)
            {
                case (("a", "b"), ("a", "b"), "a", "b", "a", "b", "a", "b"): 
                    Console.WriteLine("A");
                    break;
                default:
                    break;
            }
        }

will compile into this absolute mess:

private static void Main(string[] args)
{
    string str3 = "a";
    string str5 = "a";
    ((string, string), (string, string), string, string, string, string, string, (string)) tuple = (("a", "b"), (str3, "b"), str5, "b", "a", "b", "a", ("b"));
    if (tuple.Item1 == ("a", "b"))
    {
        (string, string) tuple3 = tuple.Item2;
        if (((((((tuple3 == ("a", "b")) && (tuple.Item3 == "a")) && (tuple.Item4 == "b")) && (tuple.Item5 == "a")) && (tuple.Item6 == "b")) && (tuple.Item7 == "a")) && (tuple.Rest == ))
        {
            Console.WriteLine("A");
        }
    }
}

 

The primary reason for the compiler doing this is to allow for simplified syntax and to catch issues where the code will seek too many different targets. When a switch statement is used the compiler will create a series of if statements if it can accomplish this reasonably. Yes, your switch statement probably serves no purpose in terms of CPU time, but it does make it easier to read. The real issue is when the compiler is forced to make a jump table, which it cannot make as effective with more than one expression to compare the targets to. The easy way around this is to limit the number of items in the expression to a single object or as few objects as possible. Tuples allow the compiler to make your input into a single variable, or just a few variables limiting the number of if statements needed and drastically improving performance if a jump table is made.

Related