155

Why is it that in a C# switch statement, for a variable used in multiple cases, you only declare it in the first case?

For example, the following throws the error "A local variable named 'variable' is already defined in this scope".

switch (Type)
{
    case Type.A:
            string variable = "x";
                break;
    case Type.B:
            string variable = "y";
                break;
}

However, per the logic, the initial declaration should not be hit if the type is Type.B. Do all variables within a switch statement exist in a single scope, and are they created/allocated before any logic is processed?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jeremy
  • 9,023
  • 20
  • 57
  • 69
  • 1
    the really ugly things is that people do this: `switch (Type) { case Type.A: string variable = "x"; break; case Type.B: variable = "y"; break; }` – giammin Jul 17 '14 at 09:04
  • 2
    @giammin: Please elaborate. – Gabe Jul 16 '15 at 10:07
  • 2
    @zazkapulsk you should first declare a variable and then use it in the switch: `string variable = null; switch (Type) { case Type.A: variable = "x"; break; case Type.B: variable = "y"; break; }` – giammin Jul 16 '15 at 13:53
  • 11
    I think is very stupid that a case isn't a new scope. Adding `case x: {...}` is very ugly. Declaring variable before the switch is even more ugly. I think it's a lack in design of c#. – Jeroen van Langen Mar 20 '18 at 13:30
  • 6
    I cannot believe what I just learned to day. I am in disbelief, this is so illogical I still cant believe. To me this is design flaw in C# as it does not make sense that a variable declared in 1st case is visible in second even if there are no brackets used. Logically, first case block should not be hit if it is not true which means the variable should be undeclared for 2nd case block to work with = error. But nope :(. Code smell? – pixel Dec 27 '18 at 18:34
  • **And still today i'm very annoyed by this design flaw!** – Jeroen van Langen Apr 16 '19 at 10:24
  • since both types are string, just dont declaretype in case Type.B switch (Type) { case Type.A: string variable = "x"; break; case Type.B: variable = "y"; break; } – Kaane Guji May 27 '20 at 17:48

7 Answers7

282

If you want a variable scoped to a particular case, simply enclose the case in its own block:

switch (Type)
{
    case Type.A:
    {
        string variable = "x";
        /* Do other stuff with variable */
    }
    break;

    case Type.B:
    {
        string variable = "y";
        /* Do other stuff with variable */
    }
    break;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 10
    A note: this won't work if the brace enclosed only the first block, but not the second. In such a case the second «variable» still issuing the error, although the scopes are different. Looks like a bug. – Hi-Angel Oct 13 '14 at 07:18
  • 3
    @Hi-Angel: that is not a bug. Multiple local variables with the same name cannot be declared inside a block or its nested blocks. In effect, a nested block 'contains' local variables that are declared in the enclosing block - even if the declaration occurs lexically later in the file. This is also true for constructs other than a switch statement. See 3.3 of the C# language spec – Michael Burr Oct 14 '14 at 05:20
  • I think I got it: if the variable exist in the block, then I can't use anymore the same name in a nested block even if the nested block resides higher of the declaration of the variable. It is irrational, and ought to be a bug, but a bug engraved in a docs becomes a feature ☺ – Hi-Angel Oct 14 '14 at 05:44
  • 1
    This should be the accepted answer. – Kris Craig Sep 20 '19 at 09:05
51

I believe it has to do with the overall scope of the variable, it is a block level scope that is defined at the switch level.

Personally if you are setting a value to something inside a switch in your example for it to really be of any benefit, you would want to declare it outside the switch anyway.

Otiel
  • 18,404
  • 16
  • 78
  • 126
Mitchel Sellers
  • 62,228
  • 14
  • 110
  • 173
  • 42
    Follow the braces. A variable only exists inside the innermost braces in which the variable is first declared. – Jarrett Meyer Oct 21 '08 at 17:02
  • 2
    As someone who's coming from the VB world, this is one of the reasons I kinda hate the `switch` statement. Other reasons include having to `break;` after each case, and that there's no equivalent for things like `Case 1, 2, 3`, `Case 4 To 10`, or `Case Is > 10`. – 41686d6564 stands w. Palestine Sep 20 '18 at 14:04
  • @AhmedAbdelhameed, you can accomplish that with switch fall through statements: https://stackoverflow.com/questions/174155/switch-statement-fallthrough-in-c – TheBuildGuy Dec 06 '18 at 23:24
  • @41686d6564standsw.Palestine: As of C#9.0 (released with .NET 5 in November 2020) the C# `switch` can now handle ranges in the `case` statements, such as `case > 10:`. See the C# language reference page on selection statements: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/selection-statements#the-switch-statement – Simon Elms Nov 18 '22 at 10:09
42

Yes, the scope is the entire switch block - unfortunately, IMO. You can always add braces within a single case, however, to create a smaller scope. As for whether they're created/allocated - the stack frame has enough space for all the local variables in a method (leaving aside the complexities of captured variables). It's not like that space is allocated during the method's execution.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    With all due respect, your Skeetness, please don't recommend adding scope to a switch case block. If you need a new scope for that block, odds are you're doing too much in the block. Instead, I suggest you recommend the processing be pushed to a function call. – Randolpho Aug 27 '10 at 14:44
  • 29
    @Randolpho: I think that's too general a statement to make. It could easily just be three or four lines, but affecting two or three local variables - enough for it to be a pain to refactor out into a method call. – Jon Skeet Aug 27 '10 at 18:19
  • 4
    Agreed, I bumped into this issue more than once, and it was for a processing logic of two or three lines... Puting that in a separate method is just pain. – Philippe Jan 16 '11 at 16:35
  • As my variable can change a type depends on the switch smaller scope worked great. up vote. – Maxim Apr 01 '13 at 22:05
  • I tried doing this, but it will give error that the variable is declared in a child scope to denote something else. – lost_in_the_source Dec 12 '13 at 22:31
  • 2
    @EdwardKarak: Only if you still declare it in a higher scope. If you use braces for *both* cases, it should be fine. – Jon Skeet Dec 13 '13 at 07:32
  • What's the reason that a `case` could be multi-statement and must end with a break/return, in contradictoin to an `if` statement which allows 1 statement or a new scope. I would suggest; lose the break and reuse the `if` statement pattern. It's so.. antipattern... – Jeroen van Langen Aug 15 '18 at 12:16
  • @J.vanLangen: Basically it's historical. The `switch` statement has a lot of historical baggage. C# 8 will have switch expressions though, which will be significantly nicer. – Jon Skeet Aug 15 '18 at 15:57
  • @JonSkeet Pity that they didn't changed the pattern before adding it to c#. I'll check the c# 8 expressions. – Jeroen van Langen Aug 21 '18 at 06:19
  • @JonSkeet If variables are scoped to the entire switch block, then **why can't I declare uninitialized variables BEFORE the first case label?** It makes no sense to have to declare its type inside of a case label if it will be used throughout the whole switch. I understand that I can't *initialize* it before 1st label, but I can't even declare it? Why? I want to limit its scope to the switch block. – geekley Jul 19 '23 at 07:27
  • @geekley: Well, the grammar of the language doesn't allow that. That doesn't in any way invalidate my statement about the scope of variables. I don't think the scope is *deliberately* the whole switch statement in order for that to be useful - it's just one of those things that has dropped out. I strongly suspect that if C# were to be designed from scratch, the scope of each variable would be in the "case body" instead of for the whole switch statement, but it's too late to change that now. – Jon Skeet Jul 19 '23 at 12:55
  • @JonSkeet You can arbitrarily `goto case` up/down (e.g. conditionally), so I'm fine with (and prefer) variables being scoped to switch block - you can think of case labels like any regular label. You have the option of using case blocks if you want local scope, and that won't be always. My only issue is not allowing declarations in a place that makes its scope clear to avoid confusion. There should be no impediments to implementing this if unassigned declarations run no code. Unless I'm missing something, like static ctor potentially being called (?). Even so, it can just run before all cases. – geekley Jul 19 '23 at 20:56
  • I've opened a [proposal](https://github.com/dotnet/csharplang/discussions/7359) to allow declaring switch-scoped variables before 1st case label. – geekley Jul 19 '23 at 22:14
14

Because their scope is at the switch block. The C# Language Specification states the following:

The scope of a local variable or constant declared in a switch block is the switch block.

Jeff B
  • 8,572
  • 17
  • 61
  • 140
itsmatt
  • 31,265
  • 10
  • 100
  • 164
0

The variables do share scope in the C# compiler. However, scope doesn't exist in the same way in CIL. As for actual creation / initialization... the .NET memory model lets the compiler move reads / writes a bit as long as simple rules are followed unless the variable is marked as volatile.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jezell
  • 2,532
  • 14
  • 12
-2

The initialization takes place in the case, but the declaration is effectively done at the top of the scope. (Psuedo-code)

switch (Type)
{
string variable;

    case Type.A:
            variable = "x";
                break;
    case Type.B:
            variable = "y";
                break;
}
James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 20
    I'm petty sure this code doesn't work – Unknown Coder Jul 22 '11 at 14:47
  • 4
    @Jim, Yes, I'm aware that code doesn't work -- which is why I referred to it as "Pseudo-Code", but it is what is "effectively" done. – James Curran Apr 14 '13 at 14:06
  • Not quite. Consider that if you remove the `break`s you end up with something like: ``` string variable = "x"; variable = "y"; ``` This is a procedural `GOTO:` for all intents and purposes. The correct code should be something like ``` string myVar; switch(myValue) { case MyEnum.A: myVar = "x"; break; case MyEnum.B: myVar = "Y"; break; } ``` – percebus Jun 20 '14 at 05:42
  • 1
    @percebus - What do you mean by "correct"? In your example, the scope of myVar would be outside of the switch block. Our point is that in these scase, the scope is just the switch block. – James Curran Jul 01 '14 at 19:25
  • the point is that contrary to popular belief, a `switch` clause is `scope`-less, unlike an `if` clause. This is one of the main reasons `switch` are often regarded as "bad code" (more like un-comprehended) Thus, declaring stuff inside the brackets still makes it available on the global scope. http://msdn.microsoft.com/en-us/library/06tc147t.aspx – percebus Nov 24 '14 at 18:04
  • @percebus, where did you take what you said in the last comment from? What's declared inside a `switch` is not accessible outside it. I agree with James, the pseudo-code seems to show what happens behind the scenes. – Andrew Sep 07 '22 at 15:17
-2

switch is a really primitive procedural implementation that has been around since the ages of C itself (even before C++).

The whole switch is a block that serves as a scope-contained GOTO: (hence the : in each case). If you took some assembler classes, that might seem familiar.

That is why switch use is most helpful when combining with Enums and not using break in every single case like

switch(mood)
{
    case Mood.BORED:
    case Mood.HAPPY:
        drink(oBeer) // will drink if bored OR happy
break;

    case Mood.SAD: // unnecessary but proofs a concept
    default:
        drink(oCoffee)
break;
}
phrogg
  • 888
  • 1
  • 13
  • 28
percebus
  • 799
  • 1
  • 8
  • 21