Check whether a string is an instance of an enum - c#

I have the following enum declaration:
enum MyType
{
Boolean,
Int,
Double,
String
}
Then if I have a string abc = "anyvalue", how can I check whether the abc value is either bool, int, double or string corresponding to a myType enumerated value?

You can use Enum.TryParse method. This will return true if the value was successfully parsed into one of the enumeration values while also outputting the parsed enumeration value.
If you want to ignore case when performing the parsing use the overload that accepts a boolean parameter.
MyType type;
if (Enum.TryParse(abc, out type))
{
// can be parsed
switch (type)
{
case MyType.Boolean: break;
case MyType.Int: break;
case MyType.Double: break;
case MyType.String: break;
}
}
It's also possible to use Enum.IsDefined but this method always does a case-sensitive search so it is less flexible than Enum.TryParse and can only be used to know if the name is defined or not.

Use this:
MyType dummy;
if(bool isEnum = Enum.TryParse(mystring,out dummy))
{
// mystring is a valid enum
}

Related

Why typeof(int).ToString() is not constant?

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();
}

Is it possible in .Net to set integer enum to arbitrary value

For some reason the HttpResponseMessageProperty which I'm using to return specific HTTP response codes to a client uses a HttpStatusCode enumeration. This enumeration does not include 422, which is not included in the enum. Is there any way to set an enumeration using an integer since that's what it is under the covers?
Yes, it's possible - internally an enum value is just an integer and you can "force" an enum value to have an invalid value and the CLR will continue on its merry way.
for example:
enum Foo {
Bar = 1,
Baz = 2
}
void Test() {
Foo foo; // as Foo is a value-type and no initial value is set, its value is zero.
Console.WriteLine( foo ); // will output "0" even though there is no enum member defined for that value
Foo foo2 = (Foo)200;
Console.WriteLine( foo2 ); // will output "200"
}
This is why, when working with enum values of unknown provenance, you should always handle the default case and handle appropriately:
public void DoSomethingWithEnum(Foo value) {
switch( value ) {
case Foo.Bar:
// do something
break;
case Foo.Baz:
// do something else
break;
default:
// I like to throw this exception in this circumstance:
throw new InvalidEnumArgumentException( "value", (int)value, typeof(Foo) );
}
}
You can cast any number into an enum, given the enum has the same base structure(usually it is int :
response.StatusCode = (HttpStatusCode)422;
Yes, you can have any value you want (as long as it is in range of enum's base type wich usally is int):
HttpStatusCode value = (HttpStatusCode)422;

Enum.TryParse strange behaviour

Why does this test pass? TestEnum doesn't contain and option with value "5". So this test should fail, but it doesn't.
private enum TestEnum
{
FirstOption = 2,
SecontOption = 3
}
[Test]
public void EnumTryParseIntValue()
{
TestEnum enumValue;
bool result = Enum.TryParse<TestEnum>(5.ToString(), out enumValue);
Assert.IsTrue(result);
}
Enum.TryParse Method (String, TEnum)
If value is a name that does not correspond to a named constant of
TEnum, the method returns false. If value is the string representation
of an integer that does not represent an underlying value of the TEnum
enumeration, the method returns an enumeration member whose underlying
value is value converted to an integral type. If this behavior is
undesirable, call the IsDefined method to ensure that a particular
string representation of an integer is actually a member of TEnum.
"Returns an enumeration member whose underlying value is value converted to an integral type"
If the value is not present you get back the integer. I don't consider getting back 5 to be an "enumeration member" but that is how it works. If you parse 2 you get FirstOption.
if (Enum.IsDefined(typeof(TestEnum), 5.ToString()))
{
result = Enum.TryParse<TestEnum>(5.ToString(), out enumValue);
Debug.WriteLine(result);
if (result)
{
Debug.WriteLine(enumValue.ToString());
}
}
Use Enum.IsDefined(Type enumType,Object value) - Returns an indication whether a constant with a specified value exists in a specified enumeration.
MSDN: Enum.IsDefined Method

Parse enum from String not working

I have a public enum declared like this:
public enum States
{
SomeState,
SomeOtherState
}
Having an arbitrary string myString which may represent any of the "States" above, I'd like to write a switch to check which one it currently is.
This is my attempt.
States state = Enum.Parse(States, myString, true);
switch (state)
{
case States.SomeState:
case States.SomeOtherState:
break;
default:
break;
}
Inside of the Enum.Parse() it tells me that the argument State is being used as a type instead that as a variable. Isn't that the correct usage? The first argument Enum.Parse is supposed to receive is Type enumType: why then it tells me it needs a variable?
Many kind replies indicates to use typeof. Unfortunately, I've already tried that but since I receive the following error I guessed it was a wrong idea.
Enum.Parse(typeof(States), myString, true);
yields:
cannot implicitly convert type 'object' to 'States'. An explicit conversion
exists.
Yes, you need to send in the Type of enum that your are parsing into (using typeof):
States state = (States)Enum.Parse(typeof(States), myString, true);
The Parse method is expecting an argument of type System.Type. Not the type States.
Here is the signature from the docs.
[ComVisibleAttribute(true)]
public static Object Parse(
Type enumType,
string value,
bool ignoreCase
)
Parse returns an object so it requires you to cast to your type after the parse.
Since it is expecting a type, change Enum.Parse(States, myString, true); to Enum.Parse(typeof(States), myString, true);
This is the correct one.
States state = Enum.Parse(typeof(States), myString, true);
switch (state)
{
case States.SomeState:
case States.SomeOtherState:
break;
default:
break;
}
States tmp;
Enum.TryParse<States>(myString,true ,out tmp);
Can make it more elegant by using extension class like so:
namespace YourClass.Models;
public static E ToEnum<E>(this object value, bool ignoreCase = true) where E : Enum
{
if(value == null)
{
throw new ArgumentNullException("Value cannot be null");
}
return (E)Enum.Parse(typeof(E), value.ToString(), ignoreCase);
}
And using it like so:
States state = myString.ToEnum<States>();

How can I use the string value of a C# enum value in a case statement?

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;
}

Categories

Resources