I was writing this code:
private static Expression<Func<Binding, bool>> ToExpression(BindingCriterion criterion)
{
switch (criterion.ChangeAction)
{
case BindingType.Inherited:
var action = (byte)ChangeAction.Inherit;
return (x => x.Action == action);
case BindingType.ExplicitValue:
var action = (byte)ChangeAction.SetValue;
return (x => x.Action == action);
default:
// TODO: Localize errors
throw new InvalidOperationException("Invalid criterion.");
}
}
And was surprised to find a compile error:
A local variable named 'action' is already defined in this scope
It was a pretty easy issue to resolve; just getting rid of the second var
did the trick.
Evidently variables declared in case
blocks have the scope of the parent switch
, but I'm curious as to why this is. Given that C# does not allow execution to fall through other cases (it requires break
, return
, throw
, or goto case
statements at the end of every case
block), it seems quite odd that it would allow variable declarations inside one case
to be used or conflict with variables in any other case
. In other words variables appear to fall through case
statements even though execution cannot. C# takes great pains to promote readability by prohibiting some constructs of other languages that are confusing or or easily abused. But this seems like it's just bound to cause confusion. Consider the following scenarios:
-
If were to change it to this:
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: return (x => x.Action == action);
I get "Use of unassigned local variable 'action'". This is confusing because in every other construct in C# that I can think of
var action = ...
would initialize the variable, but here it simply declares it. -
If I were to swap the cases like this:
case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; return (x => x.Action == action); case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action);
I get "Cannot use local variable 'action' before it is declared". So the order of the case blocks appears to be important here in a way that's not entirely obvious — Normally I could write these in any order I wish, but because the
var
must appear in the first block whereaction
is used, I have to tweakcase
blocks accordingly. -
If were to change it to this:
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; goto case BindingType.Inherited;
Then I get no error, but in a sense, it looks like the variable is being assigned a value before it's declared.
(Although I can't think of any time you'd actually want to do this — I didn't even knowgoto case
existed before today)
So my question is, why didn't the designers of C# give case
blocks their own local scope? Are there any historical or technical reasons for this?
Best Answer
I think a good reason is that in every other case, the scope of a “normal” local variable is a block delimited by braces (
{}
). The local variables that are not normal appear in a special construct before a statement (which is usually a block), like afor
loop variable or a variable declared inusing
.One more exception are local variables in LINQ query expressions, but those are completely different from normal local variable declarations, so I don't think there is a chance of confusion there.
For reference, the rules are in §3.7 Scopes of the C# spec:
(Though I'm not completely sure why is the
switch
block explicitly mentioned, since it doesn't have any special syntax for local variable declations, unlike all the other mentioned constructs.)