2

Traditional switch blocks have one scope, so the following throws a compiler error "A local variable or function named 'message' is already defined in this scope":

switch(value)
{
    case 1:
        string message = "Val: 1";
        break;
    case 2:

        string message = "Val: 2";
        break;
}

As Eric Lippert states:

A reasonable question is "why is this not legal?" A reasonable answer is "well, why should it be"? You can have it one of two ways. Either this is legal:

switch(y)
{
    case 1:  int x = 123; ... break;
    case 2:  int x = 456; ... break;
}

or this is legal:

switch(y)
{
    case 1:  int x = 123; ... break;
    case 2:  x = 456; ... break;
}

but you can't have it both ways. The designers of C# chose the second way as seeming to be the more natural way to do it.

There are other good explanations too, like this one:

I think a good reason is that in every other case, the scope of a “normal” local variable is a block delimited by braces ({}).

So then why does scoping behave differently with a type pattern matching switch block?

Animal p = new Dog();

switch(p)
{
    case Dog a:
        break;
    case Cat a: // Why is this legal?           
        break;
}
  • 1
    Well, the obvious answer is "because they can." Clearly, your last example is more useful than one that doesn't allow this. – Robert Harvey May 15 at 14:44
  • The rationale for the pattern matching design is described in detail here. – Robert Harvey May 15 at 15:04
4

The short answer is because a is a pattern variable and pattern variables are scoped to their containing block.

For example, if you proceed that switch with if (p is Dog a) return; then it'll no longer compile as it will complain that your two a variables are already defined. That's because the "containing block" for an if is the block that contains the if. However, for a pattern variable in a case label, the containing block is the case block. So in your example, those two a variables exist in separate blocks.

See Scope of pattern variables in the C# 7 docs for details.

To understand why this change to the variable scope in switches was made, consider the following code:

switch(animal)
{
    case Dog dog1 when dog1.AverageWeightKg > 20:
        // do something with dog1
        break;
    case Dog dog2 when dog2.AverageWeightKg > 10:
        // do something with dog2
        break;
    case Dog dog3:
        // do something with dog3
        break;
    case Cat cat:            
        // do something with cat
        break;
}

Those dog1, dog2 and dog3 variable names are really ugly. So whilst it created inconsistencies with other aspects of variable scope in C#, the decision was made to change the scoping rules for pattern variables. That means we can write that above code in a far more elegant fashion:

switch(animal)
{
    case Dog dog when dog.AverageWeightKg > 20:
        // do something with dog
        break;
    case Dog dog when dog.AverageWeightKg > 10:
        // do something with dog
        break;
    case Dog dog:
        // do something with dog
        break;
    case Cat cat:            
        // do something with cat
        break;
}
  • In other words, "They did it this way because they did it this way?" – Robert Harvey May 15 at 15:16
  • 3
    @RobertHarvey, No. They did it that way, despite it being very controversial at the time, because it works well in most scenarios. I argued at the time that the benefits that those inconsistent rules brought did not outweigh sullying the language with those inconsistencies. I lost the argument. Given that most developers seem happy with the language designers’ decision, it was good that I lost. – David Arno May 15 at 15:21
  • Fair enough, though it now occurs to me that the OP's example would have worked just as well if C# still insisted on different variables for each case, as Microsoft's code samples demonstrate. – Robert Harvey May 15 at 15:34
  • 1
    @RobertHarvey, Absolutely. In fact the OP's examples would read better if they'd used case Cat cat and case Dog dog . The re-use of variable names comes into its own though with when guards. case Cat cat1 when cat1.Claws == Claws.Retractable:... and case Cat cat2 ... would be ugly. Being able to use cat in both cases improves readability in my view. – David Arno May 15 at 18:41
-1

Seems to be covered in the documentation :

Without pattern matching, this code might be written as follows. The use of type pattern matching produces more compact, readable code by eliminating the need to test whether the result of a conversion is a null or to perform repeated casts.

With pattern matching:

...
    case Array arr:
       Console.WriteLine($"An array with {arr.Length} elements.");
       break;
    case IEnumerable<int> ieInt:
       Console.WriteLine($"Average: {ieInt.Average(s => s)}");
       break;   
...

Without:

...
        if (coll is Array) {
           Array arr = (Array) coll;
           Console.WriteLine($"An array with {arr.Length} elements.");
        }
        else if (coll is IEnumerable<int>) {
            IEnumerable<int> ieInt = (IEnumerable<int>) coll;
            Console.WriteLine($"Average: {ieInt.Average(s => s)}");
        }
...

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch

The switch statement is a bit of a problem child in terms of code design. You should try never to have to use it. But occasionally it can be much clearer than the alternative if blocks

  • This is a bit different than the code the OP posted, which looks more like "polymorphic casting," of sorts. – Robert Harvey May 15 at 14:53
  • its copied from the doc. ive added the before example – Ewan May 15 at 14:54
  • Your "before" example uses two different variables. Are you saying that using the same variable name, as described in the OP, will not compile? – Robert Harvey May 15 at 14:55
  • No. It will compile. The example just uses different names but the scoping is the same – Ewan May 15 at 14:57
  • 4
    Oh, please. ... – Robert Harvey May 15 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.