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;
}