I'm doing a little C# program, but it is configurable. Properties.Settings.Default.foobar is equal to F3, but that is subject to change. Originally it'd be this way:
case Keys.F3:
//dostuff
break;
But I need Properties.Settings.Default.foobar in place of F3, since it's a setting and thus subject to change.
case Keys.Properties.Settings.Default.foobar:
Obviously doesn't work, nor does putting it in parenthesis, so I'm not sure. I'm clearly new to C#, so any help?
The program uses unfocused hotkeys, but these keys are user configurable, so I can't have any sort of constant/static for a case, right?
case Keys.Properties.Settings.Default.foobar:
As you say, this obviously doesn't work. The specific reason is that the values for a case statement must be compile-time constants.
A simpler if comparison would probably be better. Since your value is a Keys and your property is a string, you'll need to convert one type to the other for comparison. I'll convert to a string for simplicity, or you could convert the string to the enum type.
if (myVar.ToString() == Properties.Settings.Default.foobar)
So instead of having, e.g.
switch (myVar) {
case Keys.F1:
// something
case Keys.F2:
// something
}
You would have:
if (myVar.ToString() == Properties.Settings.Default.foo) {
// something
} else if (myVar.ToString() == Properties.Settings.Default.bar) {
// something
}
Keys.F3 is a value of the Keys enumeration. So you'll need to convert your Properties.Settings.Default.foobar string into a value of the enumeration too.
Luckily, there is a built-in method to do that:
var foobarKey = (Keys)Enum.Parse(typeof(Keys), Properties.Settings.Default.foobar);
...
if (whatever == foobarKey)
{
...
Related
I'm trying to do this:
const string intType = typeof(int).ToString();
switch (typeof(MyT).ToString())
{
case intType:
{
return "int";
break;
}
...
}
But compiler says:
error CS0133: The expression being assigned to 'intType' must be constant
As I know, typeof operator works at compile-time. So, what's wrong?
As I know, typeof operator works at compile-time.
You don't know that because knowledge has to be true. Where did you get the idea that typeof is executed at compile time? It produces a non-constant object. And then there is no guarantee that ToString doesn't produce a different string every time it runs, so it cannot be treated as a constant either.
So, what's wrong?
You're reasoning from a false belief.
The C# specification clearly describes the conditions that must be met for an expression to be a compile-time constant. Those conditions include the expression not containing any typeof operator or method call.
But there are far bigger problems here. I assume that MyT is a generic type parameter, which means you are attempting to switch on the value of a generic type parameter. That is almost always the wrong thing to do.
What are you really trying to do? What problem are you really trying to solve? Because this code you've shown so far indicates that you're going down an unproductive path to solve whatever the real problem is.
I think, it is quite obvious, what he wants to achieve:
He wants to check for type equality in a switch-case instead of via if-elseif. And to be honest, why not? But how can he achieve this?
First option: Wait for C# 7.0. Yeah, shit like this is possible in the future!
Second option: Use strings. But the case strings need to be constant. So what about the wonderful nameof?
I just tried this "beauty" and it works, so maybe this solves your problem:
switch (typeof(Int32).Name)
{
case nameof(Int32):
Console.WriteLine("It's an Int32!");
break;
case nameof(Double):
Console.WriteLine("It's a Double");
break;
}
The only way to compare type MyT with known types is by checking their Type objects for equality. This can be done as follows:
if (typeof(MyT) == typeof(int)) return "int";
if (typeof(MyT) == typeof(decimal)) return "decimal";
// etc...
You cannot use this approach in a switch because (at the moment) a switch requires that the item being checked is of a simple type:
switch (typeof(T)) // Compile error: "switch expression or case label must be a bool,
// char, string, integral, enum, or corresponding nullable type"
{
case typeof(int): return "int";
case typeof(decimal): return "decimal";
// ...
}
Also, as others already said, checking types in this way almost always means that your approach can be improved by applying different object oriented principles.
E.g. instead of MyMethod<MyT>(MyT item) with type checks for MyT, consider making MyMethod(int item), MyMethod(decimal item) etc.
If you are just trying to get a string describing the type of object, you just need to call .GetType() instead.
For example, the following is a small function that will return the string name of the object type.
static string GetTypeString(object obj)
{
return obj.GetType().FullName;
}
This will return to the full path to the object. In int's case, it will return System.Int32. If you only want the Int32 part, use GetType().Name instead.
Also, you don't need to have a break; in a switch if you have a return;
If you have specific code that needs to be run for some types, or a specific string you want to return, you can use a string on the values returned by the above. For example:
static string GetSimpleType(object obj)
{
var stringRepresentation = GetTypeString(obj);
switch (stringRepresentation)
{
case "System.Int64":
case "System.Int32":
return "int";
default:
return stringRepresentation;
}
}
default is a catch all in switch statements for everything that does not have a case. Think of it like an else.
In the above example, we return the same value for int, Int32, and Int64. Case labels can fall through to other case labels if they are empty.
You can find all the values you need to write a switch for by running a simple script, and hard code the string values since they will always be the same for the same types. If the string is different, then the type is different.
Finally, if you are comparing types, if and if else works better:
static string GetSimpleType(object obj)
{
if (obj.GetType() == typeof(int))
{
return "int";
}
return obj.GetType().ToString();
}
For converting a string to an enum, which of the following ways is better?
This code:
colorEnum color = (colorEnum)Enum.Parse(typeof(colorEnum), "Green");
or this:
string colorString = ...
colorEnum color;
switch (colorString)
{
case "Green":
color = colorEnum.Green;
break;
case "Red":
color = colorEnum.Red;
break;
case "Orange":
color = colorEnum.Orange;
break;
....
}
You should use the Enum.TryParse, if it fails you can handle the error correctly.
sample:
ColorsEnum colorValue;
if (Enum.TryParse(colorString, out colorValue))
if (Enum.IsDefined(typeof(Colors), colorValue) | colorValue.ToString().Contains(","))
Console.WriteLine("Converted '{0}' to {1}.", colorString, colorValue.ToString());
else
Console.WriteLine("{0} is not an underlying value of the Colors enumeration.", colorString);
else
Console.WriteLine("{0} is not a member of the Colors enumeration.", colorString);
(Warning: includes plug for my own open source library...)
Personally I'd use Unconstrained Melody, which ends up with cleaner and more typesafe code:
ColorEnum color = Enums.ParseName<ColorEnum>(text);
You can use TryParseName if you suspect it may be invalid. Obviously this requires an extra library, but hopefully you'll find other things in there helpful too :)
Enum.TryParse from .NET 4 is better than the other built-in options, but:
You won't catch non-enum types at compile time, e.g. Enum.TryParse<int>(...) will still compile; Unconstrained Melody really only allows enum types
Enum.TryParse will also parse "1" (or whatever the numeric value is when converted to a string) - if you really only expect names, I think it's better to only accept names
I definitely wouldn't switch on the string values - it means if you rename the enum values, you've got to remember to rename the case value as well.
And what about Enum.TryParse<TEnum> ?
string myColorStr = "red";
colorEnum myColor;
if(!Enum.TryParse<colorEnum>(myColorStr, true, out myColor))
{
throw new InvalidOperationException("Unknown color " + myColorStr);
}
Number 1 simply on readability and maintainability. If you extend the enum you need to do no extra work, wheras with 2 you have to add more cases to the switch statement
Because you added the tag 'performance', I'm going to go with the switch.
Yes, you will have to change the cases when you rename/add/remove anything in the enum. Well that's just too bad then. Any variant of Enum.Parse/TryParse uses a lot of weird code and some reflection, just take a look inside the function with ILSpy or such. Then there is also the issue of accepting "-12354" and even a comma-separated list of valid names (resulting in all of them ORed together) even when the enum doesn't have a [Flags] attribute.
As an alternative, you could make a dictionary that translates enum names to values. It should actually be faster than the switch, because a switch on strings also goes through a dictionary but you save the actual switch part.
Obviously both ways cost some more maintenance than enum.parse and variants; whether it's worth it is up to you, since out of all of us only you have enough knowledge of the project to make the performance/coding-time trade off.
1) Is much better. It's cleaner code. You are doing in one line what would take multiple in 2). Also, it's less bug prone. When you add another item to colorEnum, you would need to remember to extend 2) wheras 1) would just work.
You may also want some error handling on the Enum.Parse.
Other than the fact that the two different code snippets doesn't do the same thing, I'd use this:
colorEnum color;
if (!colorEnum.TryParse(colorString, true, out color)
color = colorEnum.Green; // Or whatever default value you wish to have.
If you don't have .NET 4.0 then I'd do something like this:
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
return defaultValue;
return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}
This is an Extension Method to string.
Personally, while I am totally fine with the Enum.Parse solution for non-performance scenarios (read: one off run of this function occasionally ... and there are many such scenarios to be sure), I can't countenance the thought of possibly involving some reflection type method when this function needs to be performed in a loop over hundreds/thousands plus enum values at once. Gack!
So the following is a solution that gets some of the best of both worlds.
Just retrieve all values of the enum at startup time or what not, whenever it works best for you (below is one way of doing that), and then construct a Dictionary with them.
private static Dictionary<string, Color> colorDictionary;
public static Dictionary<string, Color> ColorDictionary
{
get
{
if (colorDictionary== null) {
colorDictionary = new Dictionary<string, Color>();
var all = Enum.GetValues(typeof(Color)).OfType<Color>();
foreach (var val in all)
dict.Add(val.ToString(), val);
}
return colorDictionary;
}
}
I find the switch variant horrible since you will have to modify the switch everytime you change the enum also.
I like to use the TryParse that belongs to your enum. So you can use it like this
string colorString = .....
colorEnum color;
colorEnum.TryParse(colorString, out color);
Or if you don't care about the case of the string
colorEnum.TryParse(colorString, true, out color);
The returnvalue of TryParse is true if the string was a valid enum, false if not.
On a performance point of view, as enums are implemented as static fields, the parse method will probably ends up doing refection on then enum type and try a GetField method which might be faster than the case. On the other end, if 90% of the case, the color is green, the case will be very fast... Note that the CLR sometimes rearrange code internally, changing the order of the case based on statistic (actually, I'm not sure it does that but the doc claims it could).
I use the following, it gets you all the type safety while still not falling over when you add new values into the Enum, it's also very fast.
public static colorEnum? GetColorFromString(string colorString)
{
colorEnum? retVal = null;
if(Enum.IsDefined(typeof(colorEnum), colorString))
retVal = (colorEnum)Enum.Parse(typeof(colorEnum), colorString);
return retVal;
}
My test with 8 items in the enum shows this way to be faster than the switch method.
Or else you can use (the very slow way):
public static colorEnum? GetColorFromString(string colorString)
{
foreach (colorEnum col in Enum.GetValues(typeof(colorEnum)))
{
if (col.ToString().Equals(colorString))
{
return col;
}
}
return null;
}
I've following code that uses the passed Object and acts on it after determining its type. My question is that if I convert it to use generics, will I get any performance advantage since I'd still have to determine the type of T before I act upon it? Will I save ticks on boxing/unboxing?
private void BindOptionFields(Object obj, String selectedValue, int dataLanguageID)
{
String typ = obj.GetType().Name;
switch(typ.ToLower())
{
case "ratingscaleitem":
SetRatingScale((Common.RatingScaleItem)obj, dataLanguageID, selectedValue);
break;
case "taskstatus":
text = ((TaskStatus)obj).TaskName;
optionValue = ((TaskStatus)obj).StateID.ToString();
break;
case "planlite":
text = ((XYZ.ABC.PlanLite)obj).PlanName;
optionValue = ((XYZ.ABC.PlanLite)obj).Id.ToString();
break;
case "datarow":
text = ((DataRow)obj)["TextColumn"].ToString();
optionValue = ((DataRow)obj)["KeyColumn"].ToString();
break;
default:
break;
}
if (optionValue == selectedValue)
{
selected = true;
}
}
Note: I've 3 constructors in my class that receive an ArrayList of 20 different types (similar types in ArrayList for a particular call). I loop through the arraylist and call this BindOptionFields() so if I've to write overloaded methods, I'll have to overload my constructors for 20 cases each. Can someone suggest a less tedious solution that would be fast as well?
You're misunderstanding generics.
If you convert this method to a generic method, the caller will need to specify the generic parameter at compile-time.
At that point, you can just use a normal set of overloaded methods.
Would it not be better to add a BindOptionField method (perhaps as part of an Interface e.g. IOptionFieldBindable) to the types you're switching on here?
The TaskStatus, PlanLite, and DataRow Types are good candidates particularly, since they're each providing a different way to set the same two values. That way, you could just call
(obj as IOptionFieldBindable).BindOptionField(text, optionValue);
Your solution would also be less Stringly Typed :)
I have defined a C# enum as
public enum ORDER
{
...
unknown,
partial01,
partial12,
partial23,
}
and can use its value as a string as in:
string ss = ORDER.partial01.ToString();
However when I try to use it in a case statement it fails to compile:
string value = ...
switch (value)
{
case null:
break;
case "s":
// OK
break;
case ORDER.partial01.ToString():
// compiler throws "a constant value is expected"
break;
...
I thought enums were constants. How do I get around this?
(I cannot parse the value into an enum as some of the values are outside the range)
Since C# 6, you can use: case nameof(SomeEnum.SomeValue):
Nameof is evaluated at compile time, simply to a string that matches the (unqualified) name of the given variable, type, or member. Naturally, it changes right along should you ever rename the enum option name.
Convert the string in your switch to an enum value.
(ORDER)Enum.Parse(typeof(ORDER), value, true);
The enum is a constant, but the result of .ToString() is not. As far as the compiler is concerned, it is a dynamic value. You probably need to convert your switch case into a series of if/else statements
As an alternative to using if .. else, you could convert your string to an enum first. It would probably not make much sense if the number of options is small though:
if (Enum.IsDefined(typeof(ORDER), value))
{
switch ((ORDER)Enum.Parse(typeof(ORDER), value)
{
case ORDER.partial01:
// ...
break;
case ORDER.partial12:
// etc
}
}
else
{
// Handle values not in enum here if needed
}
*sigh* if only there was a built-in T Enum.Parse<T>(string value), and a TryParse version :)
You designed this as an enum for a reason, but you're not really making use of it as an enum. Why are you taking the enum value and converting it to a string to then use in the switch instead of simply using the enum?
You said that you can't parse this in to an enum because some of the values are outside the enum range. The question to ask then is, "Why?" What is the point of having the enum if you are allowing values that aren't defined? What is it that you want to happen when you get a value that isn't defined? If it's the same thing for any undefined value, then you can use the default case. If it's not, then you can include additional cases that match the numeric representation.
If you really do get strings back, then you probably don't want to use an enum. Instead you want to create a public static class containing public string constants, which you can then use in your switch. The trick here is that the evaluation will be done in a case sensitive manner.
public static class Order
{
public const string Unknown = "Unknown";
public const string Partial01 = "Partial01";
public const string Partial12 = "Partial12";
public const string Partial23 = "Partial23";
}
string value = Order.Partial01
switch (value)
{
case Order.Partial01:
break;
default:
// Code you might want to run in case you are
// given a value that doesn't match.
break;
}
(You might also want to clean up your casing.)
As Thorarin indicated, if your switch statement can contain only enum cases, convert your string to an enum first. At least as of .Net framework 4, you can use the Enum.TryParse()<TEnum> method as defined here and do something like:
ORDER orderEnum = ORDER.unknown;
Enum.TryParse<ORDER>(value, out orderEnum);
switch (orderEnum)
{
case ORDER.unknown:
// perhaps do something to deal with cases not matching
// to known enum values, based on the string value
break;
case ORDER.partial01:
case ORDER.partial12:
case ORDER.partial23:
// map value to known cases, etc.
break;
}
Enum values are constants, but you're trying to use the results of a method (ORDER.partial01.ToString()), not a constant.
The best option, in my opinion, would be to just switch this around to using if/else if/else statements, instead of a switch. This would allow you to use the logic you are desiring.
Alternatively, if you switch your string into the enum value, you can switch on the enum values directly. You cannot switch on the enum + null + other strings, though, in one switch.
Couldn't you just instead say
case "partial01":
?
enums are constant but ToString() is a function returning a value. based on the instance of the enum object it's being called on.
That is the two statements:
ORDER.partial01.ToString()
ORDER.partial02.ToString()
calls the same function but returns two different values, so the call to the function .ToString() is in it self not a constant value.
This is not a static value as far as the compiler is concerned, since it is a function call:
ORDER.partial01.ToString()
Therefore, you can't use it as a comparison in a case statement. However, you can simply do this:
case "partial01"
That would work, since the enum value and the string are identical.
Use Extension
public static string ToGender(this Gender enumValue)
{
switch (enumValue)
{
case Gender.Female:
return "Female";
case Gender.Male:
return "Male";
default:
return null;
}
}
Example
Gender.Male.ToGender();
simply you can define a global variable
static ORDER orderstr;
now you can set the value of orderstr anywhere in the page
public enum ORDER
{
unknown,
partial01,
partial12,
partial23,
}
switch (orderstr)
{
case Order.Partial01:
break;
default:
break;
}
I have a switch statement in one class that generates Command objects, based on if a certain line in a code file matches the particular code for that Command object.
like this:
switch (line):
{
case "#CODE1":
return new Command1();
case "#CODE2":
return new Command2();
}
I want to turn it into something like this:
switch (line):
{
case Command1.Code:
return new Command1();
case Command2.Code:
return new Command2();
}
I tried it by creating a public static property of Command1, but the compiler complains...
Is there a way to do something along this intent, that the compiler won't complain about?
I think you can do this only with constants... so if it's possible for you, use constant fields instead of public static properties. The difference is that it's treated like a literal at compile time, so if the value changes, you would need to recompile all assemblies that reference the constant.
Switch only works on numeric types and string types, not properties. You can try defining a public string constant on your Command1 and Command2 class, which you should be able to switch on instead.
public class Command1
{
public string const Code = "#CODE1";
}
since you are working with strings I'm not sure if it's the best approach, but using enums works for me
enum CommandEnum { CommandOne, ComandTwo, CommandN }
//...
CommandEnum myCommand;
//...
switch (myCommand)
{
case myCommand.CommandOne:
return new Command1();
case myCommand.CommandTwo:
return new Command2();
//...
}
You could always fall back on the tried and true if / else block
if (line == Command1.Code)
return new Command1();
else if (line == Command2.Code)
return new Command2();
That's not so evil, is it?
I think your problem is you need to instantiate your objects before you can determine their property values.
I'm assuming you have a code file with tokenized instructions, like:
#CODE1
#CODE2
and you want a class to read each line of your file and then perform whichever instructions the tokens define.
You got it to work, but instead of doing a direct string comparison to the token in the file, you want the value of the token stored in a property of your objects? The problem with that approach is you have to create a new instance of your object (commands 1 and 2) before you can refer to their property values.
If I understand what you've done and what you want to do, I think your solution was fine the way it was. I'm not 100% sure I really understand what you want to do, though, or why.
HTH
If you want to check against properties on a class. I'm assuming that you probably want to be able to change those properties and check accordingly. You'll have to use an if, else if, else if type statement.
If you've got a jones to use a switch statement, you're SOL unless you're checking an integral type against constant values. if, else if, else if blocks are ugly, but it'll do what you're looking for.