Check if value is in Enum range without using IsDefined - c#

There are a few other Questions on how to convert Enums and what happens if the value parsed is out-of-range, like in:
public enum SomeTypes
{
SomeType1 = 1,
SomeType2 = 2,
SomeType3 = 3
}
public class SomeClass
{
...
var inRange = (SomeTypes) 1;
var outOfRange = (SomeTypes) 5;
...
}
Going out-of-range will not produce any error.
But I found the hard way that if you try to serialize-deserialize an enum with an out-of-range value you'll get weird errors. For example, I was getting something like
"error parsing the message or timeout exceeded"
which kept me looking for other reasons than the enum out-of-range.
Suggestions to handle this are by the means of the Enum.IsDefined. That seems to work quite nicely, but then there's this rather bold warning on msdn:
"Do not use System.Enum.IsDefined(System.Type,System.Object) for enumeration range checks as it is based on the runtime type of the enumeration, which can change from version to version."
So, my question is, can we safely use Enum.IsDefined or what is the correct way to check if the value of an enum is out-of-range without using the Enum.IsDefined?

Use Enum.GetValues():
public bool IsInRange(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return value >= values.First() && value <= values.Last();
}
[EDIT]
In case you want to check if the item is defined instead of just checking if it's inside the range, you can do similarly:
public bool IsDefined(int value){
var values = Enum.GetValues(typeof(SomeTypes)).Cast<int>().OrderBy(x => x);
return values.Contains(value);
}

There is an option for something simpler:
int value;
bool isInRange = string.IsNullOrEmpty(Enum.GetName(typeof(myEnumType), value));

I did a similar thing with objects DataContract
You must decorate the items in the list with [EnumMember] and then you could obtain the enum name with this method. So you would know if value exists in the enum cos returns his enum name.
public static string GetEnumNameFromValue(System.Type typeEnum, string value)
{
FieldInfo[] fis = typeEnum.GetFields();
foreach (FieldInfo fi in fis)
{
EnumMemberAttribute[] attributes = (EnumMemberAttribute[])fi.GetCustomAttributes(typeof(EnumMemberAttribute), false);
if (attributes.Length > 0)
{
if (string.Compare(attributes[0].Value, value, true) == 0)
{
return fi.Name;
}
}
}
return null;
}

Related

TryParsing from an object list to int | c#

I'm trying to take objects out of an object list to an int list. If the object list's value contains a string than I want to convert it to an int. the error that I'm getting is "cannot convert from 'object' to 'System.ReadOnlySpan'. I've tried looking up examples and information about lists made of objects but couldn't find anything.
I'm also at a loss as to what to do with the 'else' part of the code.
public class ListFilterer
{
public static IEnumerable(int) GetIntegersFromList(List(object) listOfItems)
{
List<int> Integers = new List<int>();
foreach (var value in listOfItems)
{
int number = 0;
bool success = Int32.TryParse(value, out number);
if (success)
{
Integers.Add(number);
}
else
{
Integers.Add(number);
}
}
return Integers;
}
}
It'll probably work out if you TryParse value.ToString() instead, if you're looking for anything that might look like an int and can be converted to an int. If you only want things that actually are ints, something like if(value is int number) should work if your c# version is recent. If it's older you may have to if(value is int) and then cast the value inside the if
Your code can be simplified to:
foreach(...){
int.TryParse(value.ToString(), out var n);
integers.Add(n);
}
Or
foreach(...){
if(value is int)
integers.Add((int)value);
else
integers.Add(0);
}
You could simply use:
var ints = listOfItems
.Select(o => { int.TryParse(o.ToString(), out int num); return num;} )
.ToList();
This will work as you wish, as if conversion fails num is 0 by default.
If Try Parse fails number is automatically 0 so you can directly write this
Int32.TryParse(value, out int number)
Integers.Add(number);
Maybe you can find the better way
var intList = objs.ConvertAll(delegate (object obj) { return (int)obj; });

Convert string into Enum [duplicate]

This question already has answers here:
Enum from string, int, etc
(7 answers)
Closed 8 years ago.
I am working on WCF service and want to return preference key as enum rather than string.
How to make enum of preferencekey column which is string type in best optimized way?
You should use the enum.Parse Method:
MyEnum myEnum = (MyEnum)Enum.Parse(typeof(MyEnum), enumString);
I found the code snippet.
Rohit,
The value of preferenceKey could not easily be converted to an enum as you would expect. The strings could be parsed using Enum.Parse() method however the enum names must be different than what you have in the database. The problems are
Your string starts with a number
Your string contains the - characters
Now that being said you could design a different approach to your naming convetion of an enum as an example (I dont like this example personally but might work).
First define a new attribute called EnumName this attribute will be used to decorate your enums with the name that you expect from the database. Such as
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public sealed class EnumNameAttribute : Attribute
{
readonly string name;
public EnumNameAttribute(string name)
{
this.name = name;
}
public string Name { get { return this.name; } }
}
The next step will be to define your enums (what I dont like is the names)
public enum Preferences
{
[EnumName("6E-SF-TYPE")]
SIX_E_SF_TYPE = 0,
[EnumName("6E-SF-VALUE")]
SIX_E_SF_VALUE = 1
}
Simple enough, we have our enum with each item decorated with the EnumName attribute. The next step will be to create a method to parse the enum based on the string from the database.
public static class EnumNameParser
{
public static Preferences ParseString(string enumString)
{
var members = typeof(Preferences).GetMembers()
.Where(x => x.GetCustomAttributes(typeof(EnumNameAttribute), false).Length > 0)
.Select(x =>
new
{
Member = x,
Attribute = x.GetCustomAttributes(typeof(EnumNameAttribute), false)[0] as EnumNameAttribute
});
foreach(var item in members)
{
if (item.Attribute.Name.Equals(enumString))
return (Preferences)Enum.Parse(typeof(Preferences), item.Member.Name);
}
throw new Exception("Enum member " + enumString + " was not found.");
}
}
This method simply takes an input stream, evaluates all the EnumNameAttributes and returns the first match (or exception if not found).
Then this can be called such as.
string enumString = "6E-SF-TYPE";
var e = EnumNameParser.ParseString(enumString);
Now if you want to take the enum and get the name for the database you can extend your helper method with
public static string GetEnumName(this Preferences preferences)
{
var memInfo = typeof(Preferences).GetMember(preferences.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(EnumNameAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((EnumNameAttribute)attrs[0]).Name;
}
throw new Exception("No enum name attribute defined");
}
I really would recommend storing the numerical values rather than the text values in the database. If you want the text values in the database too then add a table for it. Regardless, the Enum.Parse and .TryParse methods will convert a String and the .ToObject method will convert either text or numbers.
How to make enum of preferencekey column which is string type in best optimized way
Well i am afraid you cannot use your preference key as enum for 2 reasons.
your preference key is starting with integer values making it an in-valid identifier.
it also contains - which is not allowed either.
However, if you are able to convert your preference key to some valid identifiers then you can do this.
i would recomment using Enum.TryParse for parsing because
TryParse(String, Boolean, TEnum) is identical to the Parse(Type, String, Boolean) method, except that instead of throwing an exception, it returns false if the conversion fails. It eliminates the need for exception handling when parsing the string representation of an enumeration value.
Something like this
string testingString = "Test1";
SomeEnum result;
if (Enum.TryParse(testingString, out result))
{
Console.WriteLine("Success");
}
or if case sensitivity doesn't matter to you. Then you can use the overload like this
if (Enum.TryParse(testingString,true, out result))
Console.WriteLine("Success");
where SomeEnum is
public enum SomeEnum
{
Test1 = 1,
Test2 = 2
}
The simples way to do this would be:
1) create an enum representing all possible preference key values, but add a 'invalid' with value -1.
enum PreferenceKey
{
INVALID = -1,
SIX_E_SF_TYPE = 0,
SIX_E_SF_VALUE = 1,
...
}
2) create a list of strings containing the string representations of all preference key values in the same order as the enum.
private List<string> strings = new List<string> {"6E-SF-TYPE", "6E-SF-VALUE", ...};
3) Lookup the column value in the list of strings and cast the position to the enum.
If the column value cannot be found in the list of strings, IndexOf will return -1, which is the 'invalid' value.
PreferenceKey key = (PreferenceKey) strings.IndexOf(preferenceKeyColumnValue);

How can I assign an enumerated type value to a variable?

I have some factory code that creates objects based on the value of a class member representing an enum:
public enum BeltPrinterType
{
None,
ZebraQL220,
ONiel
// add more as needed
}
public static BeltPrinterType printerChoice = BeltPrinterType.None;
public class BeltPrinterFactory : IBeltPrinterFactory
{
// http://stackoverflow.com/questions/17955040/how-can-i-return-none-as-a-default-case-from-a-factory?noredirect=1#comment26241733_17955040
public IBeltPrinter NewBeltPrinter()
{
switch (printerChoice)
{
case BeltPrinterType.ZebraQL220:
return new ZebraQL220Printer();
case BeltPrinterType.ONiel:
return new ONielPrinter();
default:
return new None();
}
}
}
I need to set the value of printerChoice before I call NewBeltPrinter() - that is, if it has been changed from its default "None" value. So I'm trying to assign that value based on the string representation, but have gotten to the proverbial point of no continuance with this attempt:
string currentPrinter = AppSettings.ReadSettingsVal("beltprinter");
Type type = typeof(PrintUtils.BeltPrinterType);
foreach (FieldInfo field in type.GetFields(BindingFlags.Static | BindingFlags.Public))
{
string display = field.GetValue(null).ToString();
if (currentPrinter == display)
{
//PrintUtils.printerChoice = (type)field.GetValue(null);
PrintUtils.printerChoice = ??? what now ???
break;
}
}
I've tried everything I could think of, and have been repaid with nothing but constant reprimands from the compiler, which has pretty much been dis[mis]sing me as a knave and a sorry rascal.
Does anybody know what I should replace the question marks with?
What about just this instead of your second code block:
PrintUtils.printerChoice = (PrintUtils.BeltPrinterType)
Enum.Parse(typeof(PrintUtils.BeltPrinterType),
AppSettings.ReadSettingsVal("beltprinter"));
Use Enum.Parse to convert a string into the enum value.
(or Enum.TryParse to attempt to do it without raising an exception if it didn't parse)
edit
If you don't have an Enum.Parse available, then you will have to do the conversion yourself:
switch (stringValue)
{
case "BeltPrinterType.ONiel": enumValue = BeltPrinterType.ONiel; break;
...etc...
}
I can't compile to .NET 1.1 but this seems to work in 2.0
PrintUtils.printerChoice = (BeltPrinterType)field.GetValue(null);
EDIT : I just realized this was basically a comment in your code... But yeah I really don't see why this wouldn't work even in 1.1
From the Smart Device Framework 1.x code base:
public static object Parse(System.Type enumType, string value, bool ignoreCase)
{
//throw an exception on null value
if(value.TrimEnd(' ')=="")
{
throw new ArgumentException("value is either an empty string (\"\") or only contains white space.");
}
else
{
//type must be a derivative of enum
if(enumType.BaseType==Type.GetType("System.Enum"))
{
//remove all spaces
string[] memberNames = value.Replace(" ","").Split(',');
//collect the results
//we are cheating and using a long regardless of the underlying type of the enum
//this is so we can use ordinary operators to add up each value
//I suspect there is a more efficient way of doing this - I will update the code if there is
long returnVal = 0;
//for each of the members, add numerical value to returnVal
foreach(string thisMember in memberNames)
{
//skip this string segment if blank
if(thisMember!="")
{
try
{
if(ignoreCase)
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase).GetValue(null),returnVal.GetType(), null);
}
else
{
returnVal += (long)Convert.ChangeType(enumType.GetField(thisMember, BindingFlags.Public | BindingFlags.Static).GetValue(null),returnVal.GetType(), null);
}
}
catch
{
try
{
//try getting the numeric value supplied and converting it
returnVal += (long)Convert.ChangeType(System.Enum.ToObject(enumType, Convert.ChangeType(thisMember, System.Enum.GetUnderlyingType(enumType), null)),typeof(long),null);
}
catch
{
throw new ArgumentException("value is a name, but not one of the named constants defined for the enumeration.");
}
//
}
}
}
//return the total converted back to the correct enum type
return System.Enum.ToObject(enumType, returnVal);
}
else
{
//the type supplied does not derive from enum
throw new ArgumentException("enumType parameter is not an System.Enum");
}
}
}

Check that integer type belongs to enum member

I want to check that some integer type belongs to (an) enumeration member.
For Example,
public enum Enum1
{
member1 = 4,
member2 = 5,
member3 = 9,
member4 = 0
}
Enum1 e1 = (Enum1)4 gives me member1
Enum1 e2 = (Enum1)10 gives me nothing and I want to check it.
Use Enum.IsDefined
Enum.IsDefined(typeof(Enum1), 4) == true
but
Enum.IsDefined(typeof(Enum1), 1) == false
As Sam says, you can use IsDefined. This is somewhat awkward though. You may want to look at my Unconstrained Melody library which would let you us:
Enum1 e2 = (Enum1)10;
if (e2.IsNamedValue()) // Will return false
{
}
It's probably not worth it for a single enum call, but if you're doing a lot of stuff with enums you may find some useful things in there.
It should be quicker than Enum.IsDefined btw. It only does a linear scan at the moment, but let me know if you need that to be improved :) (Most enums are small enough that they probably wouldn't benefit from a HashSet, but we could do a binary search...)
int testNum = 5;
bool isMember = Enum.GetValues(typeof(Enum1)).Cast<int>().Any(x => x == testNum);
You look through the values of the enum and compare them to the integer.
static bool EnumTest(int testVal, Enum e)
{
bool result = false;
foreach (var val in Enum.GetValues(typeof(Enum1)))
{
if ((int)val == testVal)
{
result = true;
break;
}
}
return result;
}
Edit: Looks like Sam has a better solution.
You can use Enum.GetValues to get all defined values. Then check if your value exists in that list.
http://msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx
Be careful this won't work if you have an enum for 3 (Apples and Pears) the methods above won't detect it as valid.
[Flags]
public enum Fruit
{
Apples=1,
Pears=2,
Oranges =4,
}
Here's a succinct little snippet from an extension method I wrote a few years ago. Combines TryParse with IsDefined to do it all in one swoop and handle values that don't exist in the enum.
if (value != null)
{
TEnum result;
if (Enum.TryParse(value.ToString(), true, out result))
{
// since an out-of-range int can be cast to TEnum, double-check that result is valid
if (Enum.IsDefined(typeof(TEnum), result.ToString() ?? string.Empty))
{
return result;
}
}
}
Here's the extension for integer values
public static TEnum ParseToEnum<TEnum>(this int value, TEnum? defaultValue = null, bool useEnumDefault = false) where TEnum : struct
{
return ParseToEnumInternal(value, defaultValue, useEnumDefault);
}
And a usage
public enum Test
{
Value1 = 1,
Value2 = 3
}
var intValue = 1;
var enumParsed = intValue.ParseToEnum<Test>(); // converts to Test.Value1
intValue = 2;
enumParsed = intValue.ParseToEnum<Test>(); // either throws or converts to supplied default
enumParsed = 3.ParseToEnum<Test>(); // converts to Test.Value2
Some people don't like how it dangles off the end of the (potentially nullable) value, but I have an extension that handles null values of nullable types (int?) and I like it myself, so ...
I can post like a Gist of the whole extension method with all the overloads if you're interested.
Use:
if (Enum.IsDefined(typeof(Fruit),e2))
{
//Valid Value
}
else
{
//Invalid ENum Value
}
Found this useful. https://stackoverflow.com/a/64374930/16803533
no need to use IsDefined and No range checking

Search for a string in Enum and return the Enum

I have an enumeration:
public enum MyColours
{
Red,
Green,
Blue,
Yellow,
Fuchsia,
Aqua,
Orange
}
and I have a string:
string colour = "Red";
I want to be able to return:
MyColours.Red
from:
public MyColours GetColour(string colour)
So far i have:
public MyColours GetColours(string colour)
{
string[] colours = Enum.GetNames(typeof(MyColours));
int[] values = Enum.GetValues(typeof(MyColours));
int i;
for(int i = 0; i < colours.Length; i++)
{
if(colour.Equals(colours[i], StringComparison.Ordinal)
break;
}
int value = values[i];
// I know all the information about the matched enumeration
// but how do i convert this information into returning a
// MyColour enumeration?
}
As you can see, I'm a bit stuck. Is there anyway to select an enumerator by value. Something like:
MyColour(2)
would result in
MyColour.Green
check out System.Enum.Parse:
enum Colors {Red, Green, Blue}
// your code:
Colors color = (Colors)System.Enum.Parse(typeof(Colors), "Green");
You can cast the int to an enum
(MyColour)2
There is also the option of Enum.Parse
(MyColour)Enum.Parse(typeof(MyColour), "Red")
Given the latest and greatest changes to .NET (+ Core) and C# 7, here is the best solution:
var ignoreCase = true;
Enum.TryParse("red", ignoreCase , out MyColours colour);
colour variable can be used within the scope of Enum.TryParse
All you need is Enum.Parse.
var color = Enum.Parse<Colors>("Green");
I marked OregonGhost's answer +1, then I tried to use the iteration and realised it wasn't quite right because Enum.GetNames returns strings. You want Enum.GetValues:
public MyColours GetColours(string colour)
{
foreach (MyColours mc in Enum.GetValues(typeof(MyColours)))
if (mc.ToString() == surveySystem)
return mc;
return MyColors.Default;
}
You can use Enum.Parse to get an enum value from the name. You can iterate over all values with Enum.GetNames, and you can just cast an int to an enum to get the enum value from the int value.
Like this, for example:
public MyColours GetColours(string colour)
{
foreach (MyColours mc in Enum.GetNames(typeof(MyColours))) {
if (mc.ToString().Contains(colour)) {
return mc;
}
}
return MyColours.Red; // Default value
}
or:
public MyColours GetColours(string colour)
{
return (MyColours)Enum.Parse(typeof(MyColours), colour, true); // true = ignoreCase
}
The latter will throw an ArgumentException if the value is not found, you may want to catch it inside the function and return the default value.
Try this method.
public static class Helper
{
public static T FromStr<T>(string str) where T : struct, System.Enum
=> System.Enum.TryParse<T>(value:str,ignoreCase:true,result:out var result)
? result
: default;
public static T? FromStrNull<T>(string str) where T : struct, System.Enum
=> System.Enum.TryParse<T>(value: str,ignoreCase: true,result: out var result)
? result
: null;
}
And use it like this
var color = Helper.FromStr<MyColours>("red");
As mentioned in previous answers, you can cast directly to the underlying datatype (int -> enum type) or parse (string -> enum type).
but beware - there is no .TryParse for enums, so you WILL need a try/catch block around the parse to catch failures.
class EnumStringToInt // to search for a string in enum
{
enum Numbers{one,two,hree};
static void Main()
{
Numbers num = Numbers.one; // converting enum to string
string str = num.ToString();
//Console.WriteLine(str);
string str1 = "four";
string[] getnames = (string[])Enum.GetNames(typeof(Numbers));
int[] getnum = (int[])Enum.GetValues(typeof(Numbers));
try
{
for (int i = 0; i <= getnum.Length; i++)
{
if (str1.Equals(getnames[i]))
{
Numbers num1 = (Numbers)Enum.Parse(typeof(Numbers), str1);
Console.WriteLine("string found:{0}", num1);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Value not found!", ex);
}
}
}
One thing that might be useful to you (besides the already valid/good answers provided so far) is the StringEnum idea provided here
With this you can define your enumerations as classes (the examples are in vb.net):
< StringEnumRegisteredOnly(), DebuggerStepThrough(),
ImmutableObject(True)> Public NotInheritable Class
eAuthenticationMethod Inherits StringEnumBase(Of
eAuthenticationMethod)
Private Sub New(ByVal StrValue As String)
MyBase.New(StrValue)
End Sub
< Description("Use User Password Authentication")> Public Shared ReadOnly UsernamePassword As New eAuthenticationMethod("UP")
< Description("Use Windows Authentication")> Public Shared ReadOnly WindowsAuthentication As New eAuthenticationMethod("W")
End Class
And now you could use the this class as you would use an enum: eAuthenticationMethod.WindowsAuthentication and this would be essentially like assigning the 'W' the logical value of WindowsAuthentication (inside the enum) and if you were to view this value from a properties window (or something else that uses the System.ComponentModel.Description property) you would get "Use Windows Authentication".
I've been using this for a long time now and it makes the code more clear in intent.
(MyColours)Enum.Parse(typeof(MyColours), "red", true); // MyColours.Red
(int)((MyColours)Enum.Parse(typeof(MyColours), "red", true)); // 0
You might also want to check out some of the suggestions in this blog post:
My new little friend, Enum<T>
The post describes a way to create a very simple generic helper class which enables you to avoid the ugly casting syntax inherent with Enum.Parse - instead you end up writing something like this in your code:
MyColours colour = Enum<MyColours>.Parse(stringValue);
Or check out some of the comments in the same post which talk about using an extension method to achieve similar.

Categories