This question already has answers here:
Case Statement Block Level Declaration Space in C#
(6 answers)
Closed 12 months ago.
Found this construction while reviewing some code and I was expecting it to not compile at all, tbh. Any reasons why this is permitted?
int i = 0;
switch (i)
{
case 0:
int k = 0;
break;
case 1:
k = 1;
break;
}
Edit: even more strange, adding Console.Out.WriteLine(k); after case 1: gives error use of unassigned variable 'k'...
Any reasons why this is permitted?
We probably cannot say for certain: The essential answer is "because it is" or "because your reasons for thinking it shouldn't be differ with the thinking of those who designed the language" but we can't really speak to questions like "what were Microsoft thinking when they designed it such that...", unless perhaps someone is one of the privileged few to have sat in that design meeting and can be authoritative
SharpLab.io will, however, tell you what happens under the hood; it compiles then decompiles your code and shows you the result, so you can get an idea of what your code was transformed into by the compiler:
Note: swapped your numbers for other, non-default, ones so that identification of what was what can be maintained after the compiler changes the names
A lot of the code you write is syntactic sugar for something else; here you can see your int k isn't buried within the switch, scoped to only "within the first case", but transformed into something else entirely.. It's thus legal C# because nothing prevents it not being, and you can rationalize that in a way that will help you remember it.. In a similar way, perhaps this looks like it shouldn't work:
object o = "";
if(o is string s){
}
s = "";
s looks, to me, like it's created within the scope of the if, yet it's accessible outside the if.. You'll find a similar explanatory transformation if you run that through SharpLab..
I have the following enumerator:
enum Foo { Bar, Baz };
In the following code, the compiler aborts with the error:
use of unassigned local variable 'str'
The code:
string str;
Foo foo = Foo.Bar;
switch (foo)
{
case Foo.Bar: str = "a"; break;
case Foo.Baz: str = "b"; break;
}
str.ToLower();
The switch covers all the possible values of the enumerator. But the compiler still thinks that str might be unassigned. Why is that? Of course I could put a default case in there, but that would be wrong, since errors are not caught at compile-time. For example, if the Foo enum is modified later on and a new value added, then it would be nice to get a compiler error. If I use a default case, then the error is not caught when recompiling.
I suppose there is no way to get the compiler to accept a switch without a default case and raise an error if Foo is extended later on?
I suppose there is no way to get the compiler to accept a switch without a default case and raise an error if Foo is extended later on?
This is correct. Long story short, the reason why the compiler does this is that it is possible to assign foo a value that is not one of a valid enum Foos values by typecasting an int, making it possible to bypass all cases of the switch.
The solution that I use in situations like this is to add an assertion:
switch (foo)
{
case Foo.Bar: str = "a"; break;
case Foo.Baz: str = "b"; break;
default: Debug.Assert(false, "An enum value is not covered by switch: "+foo);
}
Enums are statically typed and type checked. But the checking does not extend to ensure that enum values only assume defined values. In fact, for Flags enums variables often do not assume any single defined value.
Like that:
Foo f = (Foo)1234; //valid
And that's why the switch can pick the default case at runtime and str could end up being used in an uninitialized state.
Some languages have stronger constructs than .NET enums such as Haskell and F#.
The enum is essentially an int and any int value could be assigned to it. This usually doesn't happen but is why you need to handle the default case or simply declare the string with a default value (like null)
Of course I could put a default case in there, but that would be wrong
That would be according to good practices. Your enum can still contain other numeric values, because enums in C# are only compile time layer above the underlying numeric representation - think const fields. Foo f = (Foo)int.MaxValue; will still compile and run, but now you don't have a switch case for it.
Depending on your interface, you may either put a default case with an exception there, define str with null, or an empty string.
your best bet is to just initialize str with an empty string in your first line. the compiler can't (or won't) try to analyse the switch logic that deeply.
This question already has answers here:
Is Switch (Case) always wrong?
(8 answers)
Closed 9 years ago.
I've come across a switch statement in the codebase I'm working on and I'm trying to figure out how to replace it with something better since switch statements are considered a code smell. However, having read through several posts on stackoverflow about replacing switch statements I can't seem to think of an effective way to replace this particular switch statement.
Its left me wondering if this particular switch statement is ok and if there are particular circumstances where switch statements are considered appropriate.
In my case the code (slightly obfuscated naturally) that I'm struggling with is like this:
private MyType DoSomething(IDataRecord reader)
{
var p = new MyType
{
Id = (int)reader[idIndex],
Name = (string)reader[nameIndex]
}
switch ((string) reader[discountTypeIndex])
{
case "A":
p.DiscountType = DiscountType.Discountable;
break;
case "B":
p.DiscountType = DiscountType.Loss;
break;
case "O":
p.DiscountType = DiscountType.Other;
break;
}
return p;
}
Can anyone suggest a way to eliminate this switch? Or is this an appropriate use of a switch? And if it is, are there other appropriate uses for switch statements? I'd really like to know where they are appropriate so I don't waste too much time trying to eliminate every switch statement I come across just because they are considered a smell in some circumstances.
Update: At the suggestion of Michael I did a bit of searching for duplication of this logic and discovered that someone had created logic in another class that effectively made the whole switch statement redundant. So in the context of this particular bit of code the switch statement was unnecessary. However, my question is more about the appropriateness of switch statements in code and whether we should always try to replace them whenever they are found so in this case I'm inclined to accept the answer that this switch statement is appropriate.
This is an appropriate use for a switch statment, as it makes the choices readable, and easy to add or subtract one.
See this link.
Switch statements (especially long ones) are considered bad, not because they are switch statements, but because their presence suggests a need to refactor.
The problem with switch statements is they create a bifurcation in your code (just like an if statement does). Each branch must be tested individually, and each branch within each branch and... well, you get the idea.
That said, the following article has some good practices on using switch statements:
http://elegantcode.com/2009/01/10/refactoring-a-switch-statement/
In the case of your code, the article in the above link suggests that, if you're performing this type of conversion from one enumeration to another, you should put your switch in its own method, and use return statements instead of the break statements. I've done this before, and the code looks much cleaner:
private DiscountType GetDiscountType(string discount)
{
switch (discount)
{
case "A": return DiscountType.Discountable;
case "B": return DiscountType.Loss;
case "O": return DiscountType.Other;
}
}
I think changing code for the sake of changing code is not best use of ones time. Changing code to make it [ more readable, faster, more efficient, etc, etc] makes sense. Don't change it merely because someone says you're doing something 'smelly'.
-Rick
This switch statement is fine. Do you guys not have any other bugs to attend to? lol
However, there is one thing I noticed... You shouldn't be using index ordinals on the IReader[] object indexer.... what if the column orders change? Try using field names i.e. reader["id"] and reader["name"]
In my opinion, it's not switch statements that are the smell, it's what's inside them. This switch statement is ok, to me, until it starts adding a couple of more cases. Then it may be worth creating a lookup table:
private static Dictionary<string, DiscountType> DiscountTypeLookup =
new Dictionary<string, DiscountType>(StringComparer.Ordinal)
{
{"A", DiscountType.Discountable},
{"B", DiscountType.Loss},
{"O", DiscountType.Other},
};
Depending on your point-of-view, this may be more or less readable.
Where things start getting smelly is if the contents of your case are more than a line or two.
Robert Harvey and Talljoe have provided excellent answers - what you have here is a mapping from a character code to an enumerated value. This is best expressed as a mapping where the details of the mapping are provided in one place, either in a map (as Talljoe suggests) or in a function that uses a switch statement (as suggested by Robert Harvey).
Both of those techniques are probably fine in this case, but I'd like to draw your attention to a design principal that may be useful here or in other similar cases. The the Open/Closed principal:
http://en.wikipedia.org/wiki/Open/closed_principle
http://www.objectmentor.com/resources/articles/ocp.pdf (make sure you read this!)
If the mapping is likely to change over time, or possibly be extended runtime (eg, through a plugin system or by reading the parts of the mapping from a database), then a using the Registry Pattern will help you adhere to the open/closed principal, in effect allowing the mapping to be extended without affecting any code that uses the mapping (as they say - open for extension, closed for modification).
I think this is a nice article on the Registry Pattern - see how the registry holds a mapping from some key to some value? In that way it's similar to your mapping expressed as a switch statement. Of course, in your case you will not be registering objects that all implement a common interface, but you should get the gist:
http://sinnema313.wordpress.com/2009/03/01/the-registry-pattern/
So, to answer the original question - the case statement is poor form as I expect the mapping from the character code to an enumerated value will be needed in multiple places in your application, so it should be factored out. The two answers I referenced give you good advice on how to do that - take your pick as to which you prefer. If, however, the mapping is likely to change over time, consider the Registry Pattern as a way insulating your code from the effects of such change.
I wouldn't use an if. An if would be less clear than the switch. The switch is telling me that you are comparing the same thing throughout.
Just to scare people, this is less clear than your code:
if (string) reader[discountTypeIndex]) == "A")
p.DiscountType = DiscountType.Discountable;
else if (string) reader[discountTypeIndex]) == "B")
p.DiscountType = DiscountType.Loss;
else if (string) reader[discountTypeIndex]) == "O")
p.DiscountType = DiscountType.Other;
This switch may be OK, you might want to look at #Talljoe suggestion.
Are switches on discount type located throughout your code? Would adding a new discount type require you to modify several such switches? If so you should look into factoring the switch out. If not, using a switch here should be safe.
If there is a lot of discount specific behavior spread throughout your program, you might want to refactor this like:
p.Discount = DiscountFactory.Create(reader[discountTypeIndex]);
Then the discount object contains all the attributes and methods related to figuring out discounts.
You are right to suspect this switch statement: any switch statement that is contingent on the type of something may be indicative of missing polymorphism (or missing subclasses).
TallJoe's dictionary is a good approach, however.
Note that if your enum and database values were integers instead of strings, or if your database values were the same as the enum names, then reflection would work, e.g. given
public enum DiscountType : int
{
Unknown = 0,
Discountable = 1,
Loss = 2,
Other = 3
}
then
p.DiscountType = Enum.Parse(typeof(DiscountType),
(string)reader[discountTypeIndex]));
would suffice.
Yes, this looks like a correct usage of switch statement.
However, I have another question for you.
Why haven't you included the default label? Throwing an Exception in the default label will make sure that the program will fail properly when you add a new discountTypeIndex and forget to modify the code.
Also, if you wanted to map a string value to an Enum, you can use Attributes and reflection.
Something like:
public enum DiscountType
{
None,
[Description("A")]
Discountable,
[Description("B")]
Loss,
[Description("O")]
Other
}
public GetDiscountType(string discountTypeIndex)
{
foreach(DiscountType type in Enum.GetValues(typeof(DiscountType))
{
//Implementing GetDescription should be easy. Search on Google.
if(string.compare(discountTypeIndex, GetDescription(type))==0)
return type;
}
throw new ArgumentException("DiscountTypeIndex " + discountTypeIndex + " is not valid.");
}
I think this depends if you are creating MType add many different places or only at this place. If you are creating MType at many places always having to switch for the dicsount type of have some other checks then this could be a code smell.
I would try to get the creation of MTypes in one single spot in your program maybe in the constructor of the MType itself or in some kind of factory method but having random parts of your program assign values could lead to somebody not knowing how the values should be and doing something wrong.
So the switch is good but maybe the switch needs to be moved more inside the creation part of your Type
I'm not absolutely opposed to switch statements, but in the case you present, I'd have at least eliminated the duplication of assigning the DiscountType; I might have instead written a function that returns a DiscountType given a string. That function could have simply had the return statements for each case, eliminating the need for a break. I find the need for breaks between switch cases very treacherous.
private MyType DoSomething(IDataRecord reader)
{
var p = new MyType
{
Id = (int)reader[idIndex],
Name = (string)reader[nameIndex]
}
p.DiscountType = FindDiscountType(reader[discountTypeIndex]);
return p;
}
private DiscountType FindDiscountType (string key) {
switch ((string) reader[discountTypeIndex])
{
case "A":
return DiscountType.Discountable;
case "B":
return DiscountType.Loss;
case "O":
return DiscountType.Other;
}
// handle the default case as appropriate
}
Pretty soon, I'd have noticed that FindDiscountType() really belongs to the DiscountType class and moved the function.
When you design a language and finally have a chance to remove the ugliest, most non-intuitive error prone syntax in the whole language.
THAT is when you try and remove a switch statement.
Just to be clear, I mean the syntax. This is something taken from C/C++ which should have been changed to conform with the more modern syntax in C#. I wholeheartedly agree with the concept of providing the switch so the compiler can optimise the jump.
I have some code like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save([Bind(Prefix="")]Person person)
{
String s = person.property;
/* ... */
}
But it throws the error: "Cannot use local variable 'person' before it is declared".
What simple thing am I missing?
It is most likely that you are receiving this error because the same variable is being declared later in the same code block.
According to compiler rules, a variable reference will refer to default by a matching declaration withing the same block EVEN IF THE SAME DECLARATION EXISTS OUTSIDE OF THE BLOCK IN IN LOGICAL SCOPE FLOW.
So in short, check to see if the variable isnt being declared later on(a couple of lines down) in the same application block.
Okay, this is just some really bizarre error - if the variable is named a particular name it does not work, for any other name it does work...
I had the same problem with a declared variable named endingYear.
Declared here:
public ChartData(MetricInfo metricInfo, MetricItem[] metricItems) : this()
{
int endingYear = 0;
Further along in the method this was not a problem:
endingYear = endingDate.Year;
But when I referenced the very same varable in a Case statement I got the "Cannot use local variable before it is declared" error even thou the variable was in intelesense:
case "QRTR_LAST_FULL_QRTR":
if (metricInfo.CalendarType == "CALENDAR")
{
switch (endingDate.Month)
{
case 1:
case 2:
case 3:
loopControl = 4;
endingYear = endingDate.Year - 1;
Based on Matt's result I tried changing the variable name to endYear and the problem went away. Very strange and a waste of a half hour or so. If it was not for this thread of posts it probably would have been a bigger time loss.
Why does the compiler say "a constant value is required" for the first case...the second case works fine...
switch (definingGroup)
{
case Properties.Settings.Default.OU_HomeOffice:
//do something
break;
case "OU=Home Office":
//do something
break;
default:
break;
}
also tried...
switch (definingGroup)
{
case Properties.Settings.Default.OU_HomeOffice.ToString():
//do something
break;
case "OU=Home Office":
//do something
break;
default:
break;
}
...same error
Here's the Properties.Setting code
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("OU=Home Office")]
public string OU_HomeOffice {
get {
return ((string)(this["OU_HomeOffice"]));
}
}
Properties.Settings.Default.OU_HomeOffice isn't a constant string - something known at compile time. The C# switch statement requires that every case is a compile-time constant.
(Apart from anything else, that's the only way it can know that there won't be any duplicates.)
See section 8.7.2 of the C# 3.0 spec for more details.
This is because the value cannot be determined at compile time (as it is coming out of a configuration setting). You need to supply values that are known at the time the code is compiled (constants).
What it's basically saying is that it needs to ensure that the value for each case will not change at runtime. Hard-coding your string in-line like you did on the second case will ensure that the value won't change at run time (as would declaring a 'const' variable and assigning it the hard-coded string as a value).
The first case is making a call into a property of a class, the value of which isn't known to the compiler at compile time.
If you have some 'configuration' values that are pretty much doing to be constant within your application, you might consider creating a class where you can hard-code these values are const variables and use those in your switch statements. Otherwise, you're likely going to be stuck with having to use if/else if statements.