Related
This question already has answers here:
Create instance of unknown Enum with string value using reflection in C#
(2 answers)
Closed 3 years ago.
What I have is a string variable that is the name of a enum. I have the integer of the enum. How would I convert that into instance of the enum itself?
Enum TestEnum
{
One = 1,
Two = 2,
Three = 3
}
string str = "TestEnum";
int intValue = 2;
I see many posts that require you to have an instance of the enum to get it's like this highly upvoted answer.
I need to do this at runtime.
Edit:
I am trying to write a class that gets and sets settings in an api that has hundreds of enums that represent settings.
The enums break are categorized by 5 basic setting types represented by five enums. These enums are like:
DoubleValueEnum
IntegerValueEnum
BooleanValueEnum
StringValueEnum
These enums are pointers to settings of type double, integer, string, bool. I believe that underneath the hood they have a database that keeps a table like this:
Type key value Represents
------- ------ ------- ---------------------------------
Double 23 2.745 DoubleValueEnum.DrawingWidth
Integer 5 18 IntegerValueEnum.PenColor
Double 54 15.9245 DoubleValueEnum.GridMajorSpacing
For doubles there is no "lower" enum that it is pointing to. For integer there is a deeper enum like "PenNumber.Red = 1, PenColor.Green = 2.
Hypothetical pen color:
Enum PenColor
{
Red = 1,
Blue = 2,
}
Each of these enums have hundreds of values. Each of these enums has a prewritten function the gets or sets the enum:
GetDoubleEnumValue(int, option)
GetIntegerValueEnum(int, option)
GetBooleanValueEnum(int, option)
GetStringValueEnum(int, option)
SetXXXXXEnumValue(enum, value)
SetDoubleEnumValue(int, int)
SetIntegerValueEnum(int, int)
SetBooleanValueEnum(int, int)
SetStringValueEnum(int, int)
Real example:
SetIntegerValueEnum ((int)IntegerValueEnum.swDxfVersion, (int)swDxfFormat_e.swDxfFormat_R14);
For your given enum definition:
enum TestEnum
{
One = 1,
Two = 2,
Three = 3
}
enum TestEnum2
{
OnePoint1 = 1,
OnePoint2 = 2,
OnePoint3 = 3
}
Use Enum.Parse(Type, string) to parse the string value as an Enum value.
string str = "Two";
TestEnum valueAsEnum = (TestEnum)Enum.Parse(typeof(TestEnum), str);
However, you need to know they type of the Enum definition that you want to parse. If the exact type of the enum value is unknown, but you do know the potential types, then you can iterate through the types and test using the Enum.TryParse
string str = "Two";
object enumValue = null;
if (!Enum.TryParse(typeof(TestEnum), str, true, out enumValue))
Enum.TryParse(typeof(TestEnum2), str, true, out enumValue))
If you do not know the specific type of the enum but you do know the class name, you can use the Type.GetType Method to resolve the type
For this to work you do need to know the full namespace of type that you want to resolve, AND the assembly that the type is defined in must already be loaded.
The syntax to convert an int to an enum is discussed here: https://stackoverflow.com/a/56859286/1690217
In this example, we know that all of the enums reside in the API.Client.Enums namespace:
string str = "TestEnum";
int intValue = 2;
string ns = "API.Client.Enums";
Type enumType = Type.GetType($"{ns}.{str}");
// now we can parse the value:
object value = Enum.ToObject(enumType, (object)intValue);
Usually we do not need to bother with a lot of this conversion just to pass values between systems, the fact that the enum resolves to an integer means that for unknown types we should be able to write our logic to just deal with the integer, and only convert it back to a specific enum when you need it.
It looks like you are trying to use BitWise operations on Enum values to allow a single setting property to represent multiple optional states.
For this sir Enums have support built in if you use the Flags attribute:
There is a good SO dicussion that covers this too: What does the [Flags] Enum Attribute mean in C#?
Lets look at PenColor enum first:
[Flag]
enum PenColor : int
{
None = 0 // 0
Red = 1 << 0, // 1
Green = 1 << 1, // 2
Blue = 1 << 2 // 4
}
By defining the discrete enums with base-2 values, we can now use either bitwise operations on the PenColor enum, or we can use simple integer addition/subtraction:
PenColor cyan = PenColor.Green | PenColor.Blue;
int cyanInt = (int)PenColor.Green + (int)PenColor.Blue;
PenColor cyanCasted = (PenColor)cyanInt;
All of those statements will be equivalent. So potentially this syntax replaces your SetIntegerValueEnum, but it relies on the enum definition being implemented with base-2 values.
To test, this statement should be true:
SetIntegerValueEnum ((int)IntegerValueEnum.swDxfVersion, (int)swDxfFormat_e.swDxfFormat_R14)
== (int)IntegerValueEnum.swDxfVersion + (int)swDxfFormat_e.swDxfFormat_R14)
== IntegerValueEnum.swDxfVersion | swDxfFormat_e.swDxfFormat_R14
The last option will only work if the [Flags] attribute decorates the enum type definition.
You can then use this in switching logic or comparisons
PenColor cyan = PenColor.Green | PenColor.Blue;
bool hasBlue = cyan & PenColor.Blue == PenColor.Blue;
// you can also use the slower Enum.HasFlag
hasBlue = cyan.HasFlag(PenColor.Blue);
I'm using Reflection to read types inside an assembly (to generate code). I can see that some enumerations should have been marked with the [Flags] attribute but whoever wrote those enums forgot to add this attribute.
Is there any reliable way of detecting when an enum can be considered a "Flags" enum?
My strategy at the moment is to read the enum in descending order, and checking if the value of element(last -1) * 2 == element(last).
That works great in most cases, except when I have enums with 0, 1, and 2 values (which could be flags or not).
edit:
Example of an enum that I'd like to detect as being flags:
public enum EnumIsFlag1
{
ItemA = 2,
ItemB = 4,
ItemC = ItemA + ItemB,
ItemD = 32,
ItemE = 64,
}
edit: My question is not a duplicate... The moderator clearly didn't read my question
Clearly, this problem can only be solved heuristically but I understand that's what you are after.
Typically, flags enums have most members with a single bit set. So I would count the number of members that have only a single bit set (e.g. that are a power of two).
Then, you can devise a heuristic such as:
//Is this a flags enum?
var totalCount = ...;
var powerOfTwoCount = ...;
if (totalCount < 3) return false; //Can't decide.
if (powerOfTwoCount >= totalCount * 0.95) return true; //Looks like flags
//Probably need some cases for small values of totalCount.
The only reason multiple bits could be set in a legitimate flags enum is combinations of flags. But the number of such enum items is usually small.
This answer goes into detail about the differences between the two, and they are very minor: just some string formatting behaviour.
The strict answer to your question is that any type can be checked for the flags enum using reflection. Anything else just needs to be checked by a human. You can check for the Flags attribute directly like this;
[Flags]
enum Foo
{
A = 0,
B = 1,
C = 4
}
enum Bar
{
A = 0,
B = 1,
C = 4
}
bool IsFlagsEnum(Type t)
{
var attr = t.GetCustomAttributes(typeof(FlagsAttribute), true).FirstOrDefault();
var result = attr != null;
return result;
}
Console.WriteLine(IsFlagsEnum(typeof(Foo))); // True
Console.WriteLine(IsFlagsEnum(typeof(Bar))); // False
I've got a enum type defined in my C# code that corresponds to all possible values for the NetConnectionStatus field in Win32_NetworkAdapter WMI table, as documented here.
The documentation shows that the integers 0 through 12 each have a unique status name, but then all integers between 13 and 65,535 are lumped into one bucket called "Other." So here's my code:
[Serializable]
public enum NetConnectionStatus
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
HardwareNotPresent = 4,
HardwareDisabled = 5,
HardwareMalfunction = 6,
MediaDisconnected = 7,
Authenticating = 8,
AuthenticationSucceeded = 9,
AuthenticationFailed = 10,
InvalidAddress = 11,
CredentialsRequired = 12,
Other
}
This works fine for the values that are not Other. For instance, I can do this:
var result = (NetConnectionStatus) 2;
Assert.AreEqual(NetConnectionStatus.Connected, result);
But for anything in that higher numeric range, it doesn't work so great. I would like it if I could do this:
var result = (NetConnectionStatus) 20;
Assert.AreEqual(NetConnectionStatus.Other, result);
But right now that result variable gets assigned the literal value 20 instead of Other. Is there some out-of-the-box way of accomplishing this, something akin to Parse() but for integers instead of strings, or perhaps some special attribute I'm unaware of? I would prefer to not write my own wrapper method for this if there is already a good way to accomplish this.
If you have a string value, then the closest thing I can think of is to use Enum.TryParse:
NetConnectionStatus result;
if (Enum.TryParse(stringValue, out result) == false)
result = NetConnectionStatus.Other;
For an integer value that you're casting, you can use:
result = (NetConnectionStatus)integerValue;
if (Enum.GetValues(typeof(NetConnectionStatus)).Contains(result) == false)
result = NetConnectionStatus.Other;
Not really ideal, but in C# enums aren't much more than fancy names for integral values, so it's valid to stuff an integer value not in the defined values of the enums into a value of that enum type.
This solution will handle negative numbers, or cases where you have gaps in your enum values more elegantly than doing numerical comparisons.
it would be nice but no. How about
var result = (NetConnectionStatus) 20;
Assert.IsTrue(result >= (int)NetConnectionStatus.Other);
.NET does not such thing as a "any other" enumeration value bucket. Technically, enumeration (enum) is a pretty set of named constants of some underlying type (which is one of following: sbyte, short, int, long and their unsigned counterparts). You can cast an enum value to/from a corresponding type without any losses, as in this example:
enum TestEnum:int // Explicitly stating a type.
{
OnlyElement=0
}
class Program
{
static void Main(string[] args)
{
// Console.WriteLine implicitly calls ToString of the TestEnum.OnlyElement.
Console.WriteLine("OnlyElement == {0}", TestEnum.OnlyElement);
//TestEnum.OnlyElement equals to 0, as demonstrated by this casting:
Console.WriteLine("(int)OnlyElement == {0}", (int)TestEnum.OnlyElement);
//We can do it in reverse...
Console.WriteLine("(TestEnum)0 == ",(TestEnum)0);
// But what happens when we try to cast a value, which is not
// representable by any of enum's named constants,
// into value of enum in question? No exception is thrown
// whatsoever: enum variable simply holds that value, and,
// having no named constant to associate it with, simply returns
// that value when attempting to "ToString"ify it:
Console.WriteLine("(TestEnum)5 == {0}", (TestEnum)5); //prints "(TestEnum)5 == 5".
Console.ReadKey();
}
}
I'd like to repeat it again, enum in .NET is simply a value of the underlying type with some nice decorations like overriden ToString method and flags checking (look here or here if you want to know more about flags). You cannot have an integer with only 14 values like "0..12 and everything else", and so you cannot have such enum. In your example, NetConnectionStatus.Other simply receives single literal value (I assume it would most probably be '13', as the next available positive value of underlying type - however it actually depends on the compiler) as any other enumeration constant would do if not specified explicitly - and, obviously, it does not become a bucket.
However, there are options to achieve simple equation checks for integers/bytes/shorts/longs - and enums alike. Consider this extension method:
static bool IsOther(this NetConnectionStatus A)
{
return (A < (NetConnectionStatus)0) || (A > (NetConnectionStatus)12);
}
Now you can have a simple assertion like this:
var result = (NetConnectionStatus)10;
Trace.Assert(result.IsOther()); //No assertion is triggered; result is NetConnectionStatus.AuthenticationFailed
and
var result = (NetConnectionStatus)20;
Trace.Assert(result.IsOther()); //Assertion failed; result is undefined!
(Of course you can replace IsOther method with IsNotOther, overload it and pretty much anything else you could do with a method.)
Now there is one more thing. Enum class itself contains a method called IsDefined, which allows you to avoid checks for specific enum's value boundaries (<0, >12), therefore preventing unwanted bugs in case enum values would ever be added/removed, at the small performance cost of unboxing and checking each value in enum for a match (I'm not sure how this works under the hood though, I hope these checks are optimized). So your method would look like this:
static bool IsOther(NetConnectionStatus A)
{
return !Enum.IsDefined(typeof(NetConnectionStatus), A);
}
(However, concluding from enum's name, it seems like you want to make a network application/server, and for these performance might be of very great importance - but most probably I'm just being paranoid and this will not be your application's bottleneck. Stability is much more of concern, and, unless you experience real troubles with performance, it is considered to be much better practice to enable as much stability&safety&portability as possible. Enum.IsDefined is much more understandable, portable and stable than the explicit boundaries checking.)
Hope that helps!
Thanks everyone for the replies. As confirmed by all of you, there is indeed no way to do this out-of-the-box. For the benefit of others I thought I'd post the (custom) code I ended up writing. I wrote an extension method that utilizes a custom attribute on the enum value that I called [CatchAll].
public class CatchAll : Attribute { }
public static class EnumExtensions
{
public static T ToEnum<T, U>(this U value) where T : struct, IConvertible where U : struct, IComparable, IConvertible, IFormattable, IComparable<U>, IEquatable<U>
{
var result = (T)Enum.ToObject(typeof(T), value);
var values = Enum.GetValues(typeof(T)).Cast<T>().ToList();
if (!values.Contains(result))
{
foreach (var enumVal in from enumVal in values
let info = typeof(T).GetField(enumVal.ToString())
let attrs = info.GetCustomAttributes(typeof(CatchAll), false)
where attrs.Length == 1
select enumVal)
{
result = enumVal;
break;
}
}
return result;
}
}
So then I just have to apply that [CatchAll] attribute to the Other value in the enum definition. Then I can do things like this:
int value = 13;
var result = value.ToEnum<NetConnectionStatus, int>();
Assert.AreEqual(NetConnectionStatus.Other, result);
And this:
ushort value = 20;
result = value.ToEnum<NetConnectionStatus, ushort>();
Assert.AreEqual(NetConnectionStatus.Other, result);
I've read a few SO posts and it seems most basic operation is missing.
public enum LoggingLevel
{
Off = 0,
Error = 1,
Warning = 2,
Info = 3,
Debug = 4,
Trace = 5
};
if (s == "LogLevel")
{
_log.LogLevel = (LoggingLevel)Convert.ToInt32("78");
_log.LogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), "78");
_log.WriteDebug(_log.LogLevel.ToString());
}
This causes no exceptions, it's happy to store 78. Is there a way to validate a value going into an enum?
Check out Enum.IsDefined
Usage:
if(Enum.IsDefined(typeof(MyEnum), value))
MyEnum a = (MyEnum)value;
This is the example from that page:
using System;
[Flags] public enum PetType
{
None = 0, Dog = 1, Cat = 2, Rodent = 4, Bird = 8, Reptile = 16, Other = 32
};
public class Example
{
public static void Main()
{
object value;
// Call IsDefined with underlying integral value of member.
value = 1;
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
// Call IsDefined with invalid underlying integral value.
value = 64;
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
// Call IsDefined with string containing member name.
value = "Rodent";
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
// Call IsDefined with a variable of type PetType.
value = PetType.Dog;
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
value = PetType.Dog | PetType.Cat;
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
// Call IsDefined with uppercase member name.
value = "None";
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
value = "NONE";
Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
// Call IsDefined with combined value
value = PetType.Dog | PetType.Bird;
Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
value = value.ToString();
Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
}
}
The example displays the following output:
// 1: True
// 64: False
// Rodent: True
// Dog: True
// Dog, Cat: False
// None: True
// NONE: False
// 9: False
// Dog, Bird: False
The above solutions do not deal with [Flags] situations.
My solution below may have some performance issues (I'm sure one could optimise in various ways) but essentially it will always prove whether an enum value is valid or not.
It relies on three assumptions:
Enum values in C# are only allowed to be int, absolutely nothing else
Enum names in C# must begin with an alphabetic character
No valid enum name can being with a minus sign: -
Calling ToString() on an enum returns either the int value if no enum (flag or not) is matched. If an allowed enum value is matched, it will print the name of the match(es).
So:
[Flags]
enum WithFlags
{
First = 1,
Second = 2,
Third = 4,
Fourth = 8
}
((WithFlags)2).ToString() ==> "Second"
((WithFlags)(2 + 4)).ToString() ==> "Second, Third"
((WithFlags)20).ToString() ==> "20"
With these two rules in mind we can assume that if the .NET Framework does its job correctly that any calls to a valid enum's ToString() method will result in something that has an alphabetic character as its first character:
public static bool IsValid<TEnum>(this TEnum enumValue)
where TEnum : struct
{
var firstChar = enumValue.ToString()[0];
return (firstChar < '0' || firstChar > '9') && firstChar != '-';
}
One could call it a "hack", but the advantages are that by relying on Microsoft's own implementation of Enum and C# standards, you're not relying on your own potentially buggy code or checks. In situations where performance is not exceptionally critical, this will save a lot of nasty switch statements or other checks!
Edit
Thanks to #ChaseMedallion for pointing out that my original implementation did not support negative values. This has been remedied and tests provided.
And the tests to back it up:
[TestClass]
public class EnumExtensionsTests
{
[Flags]
enum WithFlags
{
First = 1,
Second = 2,
Third = 4,
Fourth = 8
}
enum WithoutFlags
{
First = 1,
Second = 22,
Third = 55,
Fourth = 13,
Fifth = 127
}
enum WithoutNumbers
{
First, // 1
Second, // 2
Third, // 3
Fourth // 4
}
enum WithoutFirstNumberAssigned
{
First = 7,
Second, // 8
Third, // 9
Fourth // 10
}
enum WithNagativeNumbers
{
First = -7,
Second = -8,
Third = -9,
Fourth = -10
}
[TestMethod]
public void IsValidEnumTests()
{
Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
Assert.IsTrue(((WithFlags)(1 | 4 | 2)).IsValid());
Assert.IsTrue(((WithFlags)(2)).IsValid());
Assert.IsTrue(((WithFlags)(3)).IsValid());
Assert.IsTrue(((WithFlags)(1 + 2 + 4 + 8)).IsValid());
Assert.IsFalse(((WithFlags)(16)).IsValid());
Assert.IsFalse(((WithFlags)(17)).IsValid());
Assert.IsFalse(((WithFlags)(18)).IsValid());
Assert.IsFalse(((WithFlags)(0)).IsValid());
Assert.IsTrue(((WithoutFlags)1).IsValid());
Assert.IsTrue(((WithoutFlags)22).IsValid());
Assert.IsTrue(((WithoutFlags)(53 | 6)).IsValid()); // Will end up being Third
Assert.IsTrue(((WithoutFlags)(22 | 25 | 99)).IsValid()); // Will end up being Fifth
Assert.IsTrue(((WithoutFlags)55).IsValid());
Assert.IsTrue(((WithoutFlags)127).IsValid());
Assert.IsFalse(((WithoutFlags)48).IsValid());
Assert.IsFalse(((WithoutFlags)50).IsValid());
Assert.IsFalse(((WithoutFlags)(1 | 22)).IsValid());
Assert.IsFalse(((WithoutFlags)(9 | 27 | 4)).IsValid());
Assert.IsTrue(((WithoutNumbers)0).IsValid());
Assert.IsTrue(((WithoutNumbers)1).IsValid());
Assert.IsTrue(((WithoutNumbers)2).IsValid());
Assert.IsTrue(((WithoutNumbers)3).IsValid());
Assert.IsTrue(((WithoutNumbers)(1 | 2)).IsValid()); // Will end up being Third
Assert.IsTrue(((WithoutNumbers)(1 + 2)).IsValid()); // Will end up being Third
Assert.IsFalse(((WithoutNumbers)4).IsValid());
Assert.IsFalse(((WithoutNumbers)5).IsValid());
Assert.IsFalse(((WithoutNumbers)25).IsValid());
Assert.IsFalse(((WithoutNumbers)(1 + 2 + 3)).IsValid());
Assert.IsTrue(((WithoutFirstNumberAssigned)7).IsValid());
Assert.IsTrue(((WithoutFirstNumberAssigned)8).IsValid());
Assert.IsTrue(((WithoutFirstNumberAssigned)9).IsValid());
Assert.IsTrue(((WithoutFirstNumberAssigned)10).IsValid());
Assert.IsFalse(((WithoutFirstNumberAssigned)11).IsValid());
Assert.IsFalse(((WithoutFirstNumberAssigned)6).IsValid());
Assert.IsFalse(((WithoutFirstNumberAssigned)(7 | 9)).IsValid());
Assert.IsFalse(((WithoutFirstNumberAssigned)(8 + 10)).IsValid());
Assert.IsTrue(((WithNagativeNumbers)(-7)).IsValid());
Assert.IsTrue(((WithNagativeNumbers)(-8)).IsValid());
Assert.IsTrue(((WithNagativeNumbers)(-9)).IsValid());
Assert.IsTrue(((WithNagativeNumbers)(-10)).IsValid());
Assert.IsFalse(((WithNagativeNumbers)(-11)).IsValid());
Assert.IsFalse(((WithNagativeNumbers)(7)).IsValid());
Assert.IsFalse(((WithNagativeNumbers)(8)).IsValid());
}
}
The canonical answer would be Enum.IsDefined, but that is a: a bit slow if used in a tight loop, and b: not useful for [Flags] enums.
Personally, I'd stop worrying about that, and just switch appropriately, remembering:
if it is OK not to recognise everything (and just not do anything), then don't add a default: (or have an empty default: explaining why)
if there is a sensible default behaviour, put that in the default:
otherwise, handle the ones you know about and throw an exception for the rest:
Like so:
switch(someflag) {
case TriBool.Yes:
DoSomething();
break;
case TriBool.No:
DoSomethingElse();
break;
case TriBool.FileNotFound:
DoSomethingOther();
break;
default:
throw new ArgumentOutOfRangeException("someflag");
}
Use:
Enum.IsDefined ( typeof ( Enum ), EnumValue );
Use Enum.IsDefined.
In order to deal with [Flags] you can also use this solution from C# Cookbook:
First, add a new ALL value to your enum:
[Flags]
enum Language
{
CSharp = 1, VBNET = 2, VB6 = 4,
All = (CSharp | VBNET | VB6)
}
Then, check if the value is in ALL:
public bool HandleFlagsEnum(Language language)
{
if ((language & Language.All) == language)
{
return (true);
}
else
{
return (false);
}
}
As the others said, Enum.IsDefined returns false even if you have a valid combination of bit flags for an enum decorated with the FlagsAttribute.
Sadly, the only way to create a method returning true for valid bit flags is a bit lengthy:
public static bool ValidateEnumValue<T>(T value) where T : Enum
{
// Check if a simple value is defined in the enum.
Type enumType = typeof(T);
bool valid = Enum.IsDefined(enumType, value);
// For enums decorated with the FlagsAttribute, allow sets of flags.
if (!valid && enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true)
{
long mask = 0;
foreach (object definedValue in Enum.GetValues(enumType))
mask |= Convert.ToInt64(definedValue);
long longValue = Convert.ToInt64(value);
valid = (mask & longValue) == longValue;
}
return valid;
}
You may want to cache the results of GetCustomAttribute in a dictionary:
private static readonly Dictionary<Type, bool> _flagEnums = new Dictionary<Type, bool>();
public static bool ValidateEnumValue<T>(T value) where T : Enum
{
// Check if a simple value is defined in the enum.
Type enumType = typeof(T);
bool valid = Enum.IsDefined(enumType, value);
if (!valid)
{
// For enums decorated with the FlagsAttribute, allow sets of flags.
if (!_flagEnums.TryGetValue(enumType, out bool isFlag))
{
isFlag = enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true;
_flagEnums.Add(enumType, isFlag);
}
if (isFlag)
{
long mask = 0;
foreach (object definedValue in Enum.GetValues(enumType))
mask |= Convert.ToInt64(definedValue);
long longValue = Convert.ToInt64(value);
valid = (mask & longValue) == longValue;
}
}
return valid;
}
Note that the code above uses the new Enum constraint on T which is only available since C# 7.3. You need to pass an object value in older versions and call GetType() on it.
One way to do would be to rely on casting and enum to string conversion. When casting int to an Enum type the int is either converted to a corresponding enum value or the resulting enum just contains int as a value if enum value is not defined for the int.
enum NetworkStatus{
Unknown=0,
Active,
Slow
}
int statusCode=2;
NetworkStatus netStatus = (NetworkStatus) statusCode;
bool isDefined = netStatus.ToString() != statusCode.ToString();
Not tested for any edge cases.
I know this is an old question, but I ran into this today, and I wanted to expand on Josh Comley's answer (https://stackoverflow.com/a/23177585/3403999)
There's a couple of wrong assumptions in Josh's answer that I wanted to address:
It assumes that the '-' is always the negative sign. I don't know if there is any cultures that use a different sign, but .Net certainly allows for it in the NumberFormatInfo (https://learn.microsoft.com/en-us/dotnet/api/system.globalization.numberformatinfo.negativesign?view=net-5.0). About the only one I can think of that might be common is the parenthesis, ie (1) == -1.
Enum members have to start with an alphabetic character. Specifically, I know you can use an underscore as the first char. IE, enum MyEnum { _One = 1 } is valid.
Not really sure this exactly wrong, but it made the assumption that anything outside the range of '0' to '9' and '-' is a valid alphabetic character. It seemed like a bad assumption cause there are control characters outside that range that would return true - albeit, I don't think you can get those control characters into an enum member name without it throwing a compile error.
Anyway, here's my updated solution:
public static bool IsValid<TEnum>(this TEnum value) where TEnum : System.Enum
{
char first = value.ToString()[0];
return (char.IsLetter(first) || first == '_');
}
I did discover that you can use Unicode letters from other languages in enum member names (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/identifier-names). My solution still passes in this regard. I tested with the following enum: enum MyEnum { \u05D0 }. The enum compiled, and the IsValid returned true.
I was curious what kind of performance hit you'd take going this route vs using a static helper class with a HashSet that is filled with Enum.GetValues(typeof(TEnum)) where you check to see if the HashSet contains the enum value. The thought being that both Enum.GetValues and Enum.IsDefined are just wrappers around expensive Reflection hits, so you do the Reflection once with GetValues, cache the results, and then just check the HashSet going forward.
I ran a fairly simple test with a StopWatch and Random that would generate valid & invalid enum values, and then I ran them through 3 different methods: the ToString method, the GetValues HashSet method, and the IsDefined method. I had them do each method int.MaxValue times. The results:
ToString averaged about 2 minutes every time I ran it 2 billion times.
GetValues HashSet about 50 seconds every time I ran it 2 billion times.
IsDefined about 5 minutes every time I ran it 2 billion times.
So all the solutions recommending IsDefined are probably a bad idea if performance is a concern, or your doing a loop. If you are only using it somehow validate user input on single instances, it probably doesn't matter.
For the HashSet, it's a small performance hit for each different enum you run through it (cause the first time a new enum type gets ran through generates a new static HashSet). Not scientific, but it seemed my break even point on my PC was about 200k to 300k runs for a single enum before it started out performing using the ToString method.
The ToString method, while not the fastest had the added benefit of handling Flags enums that neither the IsDefined nor HashSet accommodate.
If performance really is a concern, don't use any of these 3 methods. Instead write a method that validates on a specific enum optimized to that enum.
Also note that my tests were with relatively small enums (5 or so elements). I don't know how performance between ToString vs HashSet once you start getting into larger enums.
class Program
{
static void Main(string[] args)
{
String value = "Two";
Type enumType = typeof(Numbers);
Numbers number = (Numbers)Enum.Parse(enumType, value);
Console.WriteLine(Enum.Parse(enumType, value));
}
public enum Numbers : int
{
One,
Two,
Three,
Four,
FirstValue = 1
}
}
This is a simplified version of an enum I use in an application. The reason to why some of the enum names doesn't have a value is because I do Enum.Parse with their names as argument, while the ones with a value is parsed from an int.
If you would step through the code above and investigate the 'number' variable, you would see that it in fact is 'Two', but the output in console is 'FirstValue'. At this point I can't see why, do you?
Okay, the solution is simple - just give the valueless enums a value. But I'm still curious.
I suspect that both FirstValue and Two have an internal value of 1, so the system doesn't know which string to output.
public enum Numbers : int
{
One, // defaults to 0
Two, // defaults to 1
Three, // defaults to 2
Four, // defaults to 3
FirstValue = 1 // forced to 1
}
There is a unique integer value for every enum value, but there is not a unique enum value for every integer value.
When you parse "two", it gets stored internally as the integer 1. Then when you try and convert it back to a string, depending on the technique used to lookup that name, you could get either "Two" or "FirstValue". As you stated, the solution is to give every enum value a defined integer value.
Here is an interesting twist to your problem, try the following Enum...
public enum Numbers : int
{
One,
Two,
Four,
FirstValue = 1
}
The console.WriteLine(...) will now print "Two"!
Both Two and FirstValue represent the same number 1 but the actual value seen depends on how the number was converted to its string representation and vice-versa.
The Enum class uses reflection to get the names of the numbers and then stores them in arrays but it sorts the the whole thing before it does so. Then Enum.ToString() does a binary search on the sorted values to get the string representation. Due to the way this is done you may get a different result depending on the number of elements you have in the enumeration!
Now as for the value "seen" in VS I suspect the debugger visualizer for enums uses an algorithm of its own which corrects(?) this bug.