Related
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.
NOTE: This is different than the proposed duplicates as this deals with an argument rather than a value. The behavior and applicable scenarios are essentially different.
Say we have SomeEnum and have a switch statement handling it like:
enum SomeEnum
{
One,
Two,
}
void someFunc(SomeEnum value)
{
switch(value)
{
case SomeEnum.One:
...
break;
case SomeEnum.Two:
...
break;
default:
throw new ??????Exception("Unhandled value: " + value.ToString());
}
}
As you see we handle all possible enum values but still keep a default throwing an exception in case a new member gets added and we want to make sure we are aware of the missing handling.
My question is: what's the right exception in such circumstances where you want to notify that the given code path is not handled/implemented or should have never been visited? We used to use NotImplementedException but it doesn't seem to be the right fit. Our next candidate is InvalidOperationException but the term doesn't sound right. What's the right one and why?
EDIT: C# 8.0 introduced switch expressions which produce compiler warnings for non-exahustive switch statements. That's another reason why you should use switch expressions over switch statements whenever applicable. The same function can be written in a safer way like:
void someFunc(SomeEnum value)
{
_ = value switch
{
SomeEnum.One => ....,
SomeEnum.Two => ....,
}
}
When a new member gets added to SomeEnum, the compiler will show the warning "CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern 'EnumHandling.SomeEnum.Three' is not covered." for the switch expression which makes it way easier to catch potential bugs.
ArgumentException looks the most correct to me in this instance (though is not defined in the BCL).
There is a specialized exception for enum arguments - InvalidEnumArgumentException:
The exception thrown when using invalid arguments that are enumerators.
An alternative is ArgumentOutOfRangeException:
The exception that is thrown when the value of an argument is outside the allowable range of values as defined by the invoked method.
The logic for using these is that the passed in argument (value) is not valid as far as someFunc is concerned.
I'd throw the InvalidEnumArgumentException as it will give more detailed information in this case, you are checking on an enum
Since you have the login in a function you can throw InvalidArgumentException.
The exception that is raised when a parameter that is not valid is
passed to a method on the referenced connection to the server.
EDIT:
A better alternative would be: ArgumentException, since InvalidArgumentException in Microsoft.SqlServer.Management.Common namespace. Something like:
throw new ArgumentException("Unhandled value: " + value.ToString());
InvalidArgumentException.
when user pass some invalid value or null value when value value is required, it is recommended to handle InvalidArgumentException .
If you were using Code Contracts (something I HIGHLY recommend), you would put this at the start of the method:
Contract.Requires(value == SomeEnum.One || value == SomeEnum.Two);
If you want to check a range of an enum which has too many individual values to write them all explicitly, you can do it like this:
Contract.Requires(SomeEnum.One <= value && value <= SomeEnum.Two);
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.
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.
Suppose we have a method that accepts a value of an enumeration. After this method checks that the value is valid, it switches over the possible values. So the question is, what is the preferred method of handling unexpected values after the value range has been validated?
For example:
enum Mood { Happy, Sad }
public void PrintMood(Mood mood)
{
if (!Enum.IsDefined(typeof(Mood), mood))
{
throw new ArgumentOutOfRangeException("mood");
}
switch (mood)
{
case Happy: Console.WriteLine("I am happy"); break;
case Sad: Console.WriteLine("I am sad"); break;
default: // what should we do here?
}
What is the preferred method of handling the default case?
Leave a comment like // can never happen
Debug.Fail() (or Debug.Assert(false))
throw new NotImplementedException() (or any other exception)
Some other way I haven't thought of
I guess most of the above answers are valid, but I'm not sure any are correct.
The correct answer is, you very rarely switch in an OO language, it indicates you are doing your OO wrong. In this case, it's a perfect indication that your Enum class has problems.
You should just be calling Console.WriteLine(mood.moodMessage()), and defining moodMessage for each of the states.
If a new state is added--All Your Code Should Adapt Automatically, nothing should fail, throw an exception or need changes.
Edit: response to comment.
In your example, to be "Good OO" the functionality of the file mode would be controlled by the FileMode object. It could contain a delegate object with "open, read, write..." operations that are different for each FileMode, so File.open("name", FileMode.Create) could be implemented as (sorry about the lack of familiarity with the API):
open(String name, FileMode mode) {
// May throw an exception if, for instance, mode is Open and file doesn't exist
// May also create the file depending on Mode
FileHandle fh = mode.getHandle(name);
... code to actually open fh here...
// Let Truncate and append do their special handling
mode.setPosition(fh);
}
This is much neater than trying to do it with switches... (by the way, the methods would be both package-private and possibly delegated to "Mode" classes)
When OO is done well, every single method looks like a few lines of really understandable, simple code--TOO simple. You always get the feeling that there is some big messy "Cheese Nucleus" holding together all the little nacho objects, but you can't ever find it--it's nachos all the way down...
I prefer to throw new NotImplementedException("Unhandled Mood: " + mood). The point is that the enumeration may change in the future, and this method may not be updated accordingly. Throwing an exception seems to be the safest method.
I don't like the Debug.Fail() method, because the method may be part of a library, and the new values might not be tested in debug mode. Other applications using that library can face weird runtime behaviour in that case, while in the case of throwing an exception the error will be known immediately.
Note: NotImplementedException exists in commons.lang.
In Java, the standard way is to throw an AssertionError, for two reasons:
This ensures that even if asserts are disabled, an error is thrown.
You're asserting that there are no other enum values, so AssertionError documents your assumptions better than NotImplementedException (which Java doesn't have anyway).
My opinion is that since it is a programmer error you should either assert on it or throw a RuntimException (Java, or whatever the equivalent is for other languages). I have my own UnhandledEnumException that extends from RuntimeException that I use for this.
The correct program response would be to die in a manner that will allow the developer to easily spot the problem. mmyers and JaredPar both gave good ways to do that.
Why die? That seems so extreme!
The reason being that if you're not handling an enum value properly and just fall through, you're putting your program into an unexpected state. Once you're in an unexpected state, who knows what's going on. This can lead to bad data, errors that are harder to track down, or even security vulnerabilities.
Also, if the program dies, there's a much greater chance that you're going to catch it in QA and thus it doesn't even go out the door.
For pretty much every switch statement in my code base, I have the following default case
switch( value ) {
...
default:
Contract.InvalidEnumValue(value);
}
The method will throw an exception detailing the value of the enum at the point an error was detected.
public static void InvalidEnumValue<T>(T value) where T: struct
{
ThrowIfFalse(typeof(T).IsEnum, "Expected an enum type");
Violation("Invalid Enum value of Type {0} : {1}", new object[] { typeof(T).Name, value });
}
For C#, something worth knowing is that Enum.IsDefined() is dangerous. You can't rely on it like you are. Getting something not of the expected values is a good case for throwing an exception and dying loudly.
In Java, it's different because enums are classes not integers so you really can't get unexpected values (unless the enum is updated and your switch statement isn't), which is one big reason why I much prefer Java enums. You also have to cater for null values. But getting a non-null case you don't recognize is a good case for throwing an exception too.
You could have a trace for the default calling out the value of the passed enum. Throwing exceptions is OK but in a large application there will be several places where your code does not care about other values of the enum.
So, unless you are sure that the code intends to handle all possible values of the enum, you'll have to go back later and remove the exception.
This is one of those questions that proves why test driven development is so important.
In this case I'd go for a NotSupportedException because literally the value was unhandled and therefore not supported. A NotImplementedException gives more the idea of: "This is not finished yet" ;)
The calling code should be able to handle a situation like this and unit tests can be created to easily test these kind of situations.
Call it an opinion or a preference, but the idea behind the enum is that it represents the full list of possible values. If an "unexpected value" is being passed around in code, then the enum (or purpose behind the enum) is not up to date. My personal preference is that every enum carry a default assignment of Undefined. Given that the enum is a defined list, it should never be out-of-date with your consuming code.
As far as what to do if your function is getting either an unexpected value or Undefined in my case, a generic answer doesn't seem possible. For me, it depends on the context of the reason for evaluating the enum value: is it a situation where code execution should halt, or can a default value be used?
It is the responsibility of the calling function to provide valid input and implicitely anything not in the enum is invalid (Pragmatic programmer seems to imply this). That said, this implies that any time you change your enum, you must change ALL code that accepts it as input (and SOME code that yields it as output). But that is probably true anyways. If you have an enum that changes often, you probably should be using something other than an enum, considering that enums are normally compile-time entities.
I usually try to define undefined value (0):
enum Mood { Undefined = 0, Happy, Sad }
That way I can always say:
switch (mood)
{
case Happy: Console.WriteLine("I am happy"); break;
case Sad: Console.WriteLine("I am sad"); break;
case Undefined: // handle undefined case
default: // at this point it is obvious that there is an unknown problem
// -> throw -> die :-)
}
This is at least how I usually do this.