Variable Declaration in a C# Switch Statement

Variable declaration in a C# switch statement

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.

How is switch variable declaration scoped?

Question is, how is x scoped inside each case without any visible blocks. meanwhile, variable invalid cant be declared in different switch cases. it has to be inside a block.

Variables introduced via pattern matching in case labels only have the scope of the body of that case.

Variables introduced "normally" in the case bodies have the scope of the whole switch statement.

Yes, it's inconsistent - but I'd argue that:

  • It's particularly useful to be able to introduce multiple variables with the same name via pattern matching
  • The scoping of variables introduced in case statements was a design mistake to start with, and this is just preventing the mistake from going any further

Note that you can't declare the same variable multiple times using pattern matches for cases which use the same case block. For example, with a simplification of your code, this is fine:

object o = null;
switch (o)
{
case Type x when x == typeof(byte):
break;
case Type x when x == typeof(short):
break;
}

But this isn't:

object o = null;
switch (o)
{
case Type x when x == typeof(byte):
case Type x when x == typeof(short):
break;
}

Arguably the compiler could have some rules to allow you to introduce multiple variables so long as they're of the same type - that could be really handy for common code. But it would definitely make the language even more complicated...


As an example of the "design mistake" point, the C# 5 specification actually has an error due to it. The C# 5 spec (8.7.2) claims:

The “no fall through” rule prevents a common class of bugs that occur in C and C++ when break statements are accidentally omitted. In addition, because of this rule, the switch sections of a switch statement can be arbitrarily rearranged without affecting the behavior of the statement.

This "arbitrary rearrangement" is untrue in C# 7 due to pattern matching ordering anyway, but it's always been untrue. Consider this code:

class Test
{
static void Main(string[] args)
{
switch (args.Length)
{
case 0:
string x;
break;
case 1:
x = args[0];
break;
}
}
}

That's valid due to the odd scoping rules - x is in scope and usable in the "case 1" block. If you rearrange the cases, however:

class Test
{
static void Main(string[] args)
{
switch (args.Length)
{
case 1:
x = args[0]; // Invalid
break;
case 0:
string x;
break;
}
}
}

... this now gives a compile-time error. The variable is still in scope (the compiler knows what you mean by x) but you can't assign a value to a local variable before its declaration.

As far as I'm aware no-one ever wants to use a variable declared by an earlier scope - it would have made much more sense either for each case block to introduce a new variable declaration space, or for C# to require braces for the case block anyway.

C# Switch/case share the same scope?

In C# the scope is determined solely by braces. If there are none, there is no separate scope. With switch/case there is obviously none. What you call »region of execution« has nothing to do at all with where you can refer to a variable. For a contrived example:

int x = 1;
goto foo;
// This part gets never executed but you can legally refer to x here.
foo:

You can do the following, though, if you like:

switch (temp)
{
case "1":
{
int tmpint = 1;
break;
}
case "2":
{
int tmpint = 1;
break;
}
}

In fact, for some switch statements I do that, because it makes life much easier by not polluting other cases. I miss Pascal sometimes ;-)

Regarding your attempted fallthrough, you have to make that explicit in C# with goto case "2".

How to use a variable assigned inside a switch statement further on in the program?

You are having issues because you're re-declaring the path variable again in the switch statement. Declare it once only outside the switch statement then assign it in the switch statement.

void MyReadString()
{
Scene currentScene = SceneManager.GetActiveScene(); // Unity command to get the current scene info
int buildIndex = currentScene.buildIndex; // Unity command to get the current scene number
string path = null;
// Create an empty string variable to hold the path information of text files
switch (buildIndex)
{
case 0:
path = "Assets/Scripts/some.txt"; //It says here that the variable path is assigned but never used!
break;
case 1:
path = "Assets/Scripts/another.txt"; //Same here - assigned but never used.
break;
// and many other cases - atleast 6 more
}
StreamReader reader = new StreamReader(path); // To read the text file // And further string operations here - such as using a delimiter, finding the number of values in the text etc
}

Unrelated, but please note that this is not how to read a file in Unity.That code will fail when you build the project. Use the Resources API or use Assetbundles.

Declare different type of variables using switch

You must declare x outside switch. and declare it only once. and if classes does not have same parent you must use dynamic as a type of x.

ParentClass x = null;// dynamic x = null; in case that x is not known type
switch (request)
{
case "ClassA":
{
x = new ClassA();
break;
}
case "ClassB":
{
x = new ClassB();
break;
}
case "ClassC":
{
x = new ClassC();
break;
}
case "ClassD":
{
x = new ClassD();
break;
}
default:
break;
}

Switch variable to use externally

Declare variable outside switch block and assign value to it. Also try not to use List suffix for collection names:

IEnumerable<Product> sortedProducts;

switch (orderBy)
{
case "lowestToBiggest":
sortedProducts = products.OrderBy(x => x.minProductPrice);
break;
case "biggestToLowest":
sortedProducts = products.OrderBy(x => x.maxProductPrice);
break;
default:
sortedProducts = products.OrderBy(x => x.minProductPrice);
break;
}

Actually your code can be simplified to:

IEnumerable<Product> sortedProducts =
products.OrderBy(p => orderBy == "biggestToLowest" ?
p.maxProductPrice :
p.minProductPrice);

Why are variables that are declared in one case statement in scope for other cases?

What @JonSkeet said is correct: A "block" in the C/C++/C# sense is not the same thing as a "case" in the logical sense. But he didn't mention the "best practice" for fixing the issue: If you want to declare a variable inside one case of a switch statement, it's good practice to wrap the whole logical "case" in a block. This way the logical "case" and the actual "scope" recognized by the compiler will be one and the same.

public static void Main()
{
var x = 2;
switch (x)
{
case 1: { // notice these braces I added
var foo = "one";
Console.Out.WriteLine(foo);
break;
}
case 2:
foo = "two"; // hooray! foo is no longer in scope here
Console.Out.WriteLine(foo);
break;
}
}


Related Topics



Leave a reply



Submit