Let's say I have this enum:
[Flags]
public enum SomeType
{
Val1 = 0,
Val2 = 1,
Val3 = 2,
Val4 = 4,
Val5 = 8,
Val6 = 16,
All = Val1 | Val2 | Val3 | Val4 | Val5 | Val6
}
and some variables:
SomeType easyType = SomeType.Val1 | SomeType.Val2;
SomeType complexType = SomeType.All;
If I want to loop through values of the first enum I can simply do:
foreach(string s in easyType.ToString().Split(','))
{ ... }
However, when I try to apply the same approach to the 'complexType' I get value 'All', which is of course valid because it's also one of possible values of the enum. But, is there a neat way to actually see of what values is the SomeType.All created of? I know I could make a manual loop through all the values like that:
if(complexType.HasFlag(ManualType.Val1) && ...
var result = string.Join(",",
Enum.GetValues(typeof(SomeType))
.Cast<SomeType>()
.Where(v => complexType.HasFlag(v)));
You can write an extension method to avoid repeating yourself.
Maybe you need to enumerate the enum values and test each one:
foreach (SomeType item in Enum.GetValues (typeof (SomeType))
{
if ((complexType & item) == item)
{
//...
}
}
Here's one possible way to do it, building on Danny Chen's answer:
public IEnumerable<T> GetFlags<T>(Predicate<int> hasFlag)
{
return GetEnumFlags<T>().Where(f => hasFlag(f)).Cast<T>();
}
private IEnumerable<int> GetEnumFlags<T>()
{
return Enum.GetValues(typeof(T)).Cast<int>().Where(IsPowerOfTwoOrZero);
}
private bool IsPowerOfTwoOrZero(int v)
{
return ((v & (v-1)) == 0);
}
Usage:
void Main()
{
SomeType easyType = SomeType.Val1 | SomeType.Val2;
SomeType complexType = SomeType.All;
GetFlags<SomeType>(v => easyType.HasFlag((SomeType)v));
GetFlags<SomeType>(v => complexType.HasFlag((SomeType)v));
}
Note this will work for Enums based on types castable to int. You can create similar methods for long etc.
If you're using an enum to represent data that has some deeper level of complexity (arbitrary groups of elements, "darkness" of colour, etc.) and struggling because of that, I'd suggest that an enum is the wrong programming construct to be using.
Using an enum for these tasks will always be error prone: if I add a new colour to your enum, I have to remember to add it to "Bright" or "Dark" too. That makes your code just as error prone for developers as the original problem. What you could do, however, is define a class or struct for colour that has a property indicating whether it's bright or not. Then you'll have clear, understandable functionality - implemented in a way that doesn't give spurious results as when you're trying to misuse a simpler language feature.
That, for example, is why Color is a struct...
Related
There is a nice way of figuring out the enumeration element using the following approach:
// memberType is enum type
if (Enum.IsDefined(memberType, valueString))
{
return Enum.Parse(memberType, valueString);
}
else
{
try
{
var underlyingValue = Convert.ChangeType(valueString, Enum.GetUnderlyingType(memberType));
if (Enum.IsDefined(memberType, underlyingValue))
{
return underlyingValue;
}
}
catch...
}
This works like charm. Except for values built from enumerations marked with FlagsAttribute. For example, for this enum and a value:
[Flags]
enum MyEnum {
One = 0x1,
Two = One << 1,
Four = One << 2,
Eight = One << 3
}
var e = MyEnum.One | MyEnum.Eight;
the approach above doesn't work. Looks like the only way to make it work is to try to get all the enum values and bitwise AND them with the input value. That's somewhat tedious though. So do you know any better way?
Answer:
The final method looks like this:
var parsed = Enum.Parse(memberType, valueString);
decimal d;
if (!decimal.TryParse(parsed.ToString(), out d))
{
return parsed;
}
throw new ArgumentOutOfRangeException(memberInfo.Name, valueString, "Bad configuration parameter value.");
I guess a better question to ask, how to detect bad values.
Looks like there is a nice work around found in C# 4.0 in a Nutshell. From here. Once you Parse the integer value to the enum, you can use this and see if the value is valid. This will work for combined flags.
static bool IsFlagDefined(Enum e)
{
decimal d;
return !decimal.TryParse(e.ToString(), out d);
}
This is expected behavior, as you can get values that do not correspond to the flags. For example, let's assume value a = 1, b = 2, c = 4, d= 8, etc. (Just standard binary progression). It is possible to have a 5 for the value (a & c) or 7 (a, b & c).
Why don't you just use Enum.Parse()
var test = MyEnum.One | MyEnum.Eight;
var str = test.ToString();
var back = (MyEnum) enum.Parse(typeof(MyEnum), str); // this returns MyEnum.One | MyEnum.Eight
First try to convert your string to "undelying type", if succeded - cast it to MyEnum and that's what you needed.
If convert failed - try to use Enum.Parse.
If it also fails - that is a very bad input (throw exception)
Update:
No testing for int conversion is needed enum.Parse(typeof(MyEnum), "9") returns MyEnum.One | MyEnum.Eight (tested in Framework 2.0 to 4.0)
Update 2:
So question really was "How to figure out bad values?". Dirty solution:
var res = Enum.Parse(targetType, str);
bool isBad;
try
{
Convert(res.ToString(), underType);
isBad = true;
}
catch
{
// convert failed
isBad = false;
}
if (isBad)
throw new Exeption();
return res;
But it is very dirty and throwing exception is expensive operation so there is performance penalty here... But it works)
Use Enum.Parse. Then check on the returned value, that no bits are set that are not valid flags. To do this, make a bitmask containing all the valid values, and OR them together with the value. If the result differs from the mask, some bits was set, that are not valid flags. This is not too tedious (but you will need to check whether the Enum is in fact a Flags enum, before applying this logic - if it is not a Flags enum, use IsDefined).
The code could go something like this; and you could store the mask per type pre-computed if you think it might be too expensive to calculate each time (it's probably not, but will depend on your case):
object value = Enum.Parse(memberType, valueString);
int numericValue = (int)value;
int definedMask = Enum.GetValues(memberType).Cast<int>().Aggregate(0, (v,a) => v | a);
if ((definedMask | numericValue) != definedMask)
throw new InvalidOperationException(String.Format("{0} is not a valid {1} value.", valueString, memberType.Name));
return value;
That is a nice question, your suggestion
try to get all the enum values and
bitwise AND them with the input value
after 10 minutes of research looks really reasonable.. ^^
I found a nice link regarding similar issue, a guy there spent quite some time to write this article, maybe you will find it interesting too.
Breaking down C# Flags enums into individual values for comparison
I need to convert a List of enums values to a single string to store in my db; then convert back again when I retrieve from the database.
Each enum's value is currently a simple integer, so it feels a bit overkill to create an extra table to deal with this.
So, in the example below, if the user selects Mother, Father and Sister, then the value stored in the database will be "0,1,3"
public enum MyEnum
{
Mother = 0,
Father = 1,
Gran = 2,
Sister = 3,
Brother = 4
}
I'm new to c#, so not sure if there is a nice out-the-box way to do this - I couldn't find anything obvious when google hunting!
Cheers in advance :)
- L
Enum's are explicitely able to be cast to/from integers
int value = (int)MyEnum.Mother;
and
MyEnum value = (MyEnum)1;
For strings use ToString and Enum.Parse
string value = MyEnum.Mother.ToString();
and
MyEnum value = (MyEnum)Enum.Parse(typeof(MyEnum),"Mother");
If you change you enum values to:
[Flags]
public enum MyEnum
{
Mother = 1,
Father = 2,
Gran = 4,
Sister = 8,
Brother = 16,
}
Then you could store Father and Gran as 6
Sister and Brother as 24 etc
by using binary numbers you should not get duplicate values by combining them
The following will convert back and forth between an array of Enum values via "0,1,3" as requested:
MyEnum[] selection = { MyEnum.Mother, MyEnum.Father, MyEnum.Sister };
string str = string.Join(",", selection.Cast<int>());
MyEnum[] enm = str.Split(',').Select(s => int.Parse(s)).Cast<MyEnum>().ToArray();
from your code it is
MyEnum a = MyEnum.Mother;
string thestring = a.ToString();
MyEnum b = (MyEnum) Enum.Parse(typeof(MyEnum), thestring);
Just use ToString to convert to the name, and the use Enum.TryParse (or Enum.Parse if you're not on .NET 4) to convert back.
If you're wanting one enum field to contain multiple values (e.g MyEnum.Mother | MyEnum.Father), you'll need to convert that to a Flags enum, as #WraithNath suggested. Otherwise you're talking about storing each option separately (there's no way to store Mother and Father in the same field with your current setup).
String Equivelant
MyEnum value = MyEnum.Father;
value.ToString(); // prints Father
Parsing
(MyEnum)Enum.Parse(typeof(MyEnum), "Father"); // returns MyEnum.Father
Enums can be cast to and from integer values. That's probably your best bet.
If you really want to use strings, ToString will return "Mother" "Father" "Gran" etc. Casting back from a string would just be a function:
private MyEnum GetEnumValue(string fromDB)
{
if( fromDB == "Mother" ) return MyEnum.Mother;
else if( fromDB == "Father") return MyEnum.Father;
//etc. etc.
}
EDIT:
Ian's answer for casting back is the more "C#-ey" way of doing it, and is far more extensible (handles adding new values to the enum much more flexibly).
Further to Jamiec's suggestion (which fits well for your need), if you need to defensively code your casting, try:
if (Enum.IsDefined(typeof(MyEnum), <your database value>))
{
// Safe to convert your enumeration at this point:
MyEnum value = (MyEnum)1;
}
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.
Why is this even possible? Is it a bug?
using System;
public class InvalidEnumParse
{
public enum Number
{
One,
Two,
Three,
Four
}
public static void Main()
{
string input = "761";
Number number = (Number)Enum.Parse(typeof(Number), input);
Console.WriteLine(number); //outputs 761
}
}
That's just the way enums work in .NET. The enum isn't a restrictive set of values, it's really just a set of names for numbers (and a type to collect those names together) - and I agree that's a pain sometimes.
If you want to test whether a value is really defined in the enum, you can use Enum.IsDefined after parsing it. If you want to do this in a more type-safe manner, you might want to look at my Unconstrained Melody project which contains a bunch of constrained generic methods.
If you have a enum with [Flags] attribute, you can have any value combination. For instance:
[Flags]
enum Test
{
A = 1,
B = 2,
C = 4,
D = 8
}
You could to do this:
Test sample = (Test)7;
foreach (Test test in Enum.GetValues(typeof(Test)))
{
Console.WriteLine("Sample does{0} contains {1}",
(sample & test) == test ? "": " not", test);
}
we have a debate about a BEST PRACTISE from a .NET architecture DESIGN POINT OF VIEW:
Task: How to manage role based visibility in UI with enum?
For example: I want to show all team types [a,b,c,d,e] to administrator but only team types [a,b,c] to normal user.
First I have enum which includes all team types:
public enum TeamType { a, b, c, d, e }
I use it like this:
if (IsAdminitrator())
comboboxTeamtypes.Items.AddRange(Enum.GetNames(typeof(TeamType)));
Now I should decide how to implement else clause. Because enums can't be inherited I have two alternative approaches:
1) Should I introduce an other role specific enum:
public enum TeamTypeOthers {a, b, c }
and then I would have:
else
comboboxTeamtypes.Items.AddRange(Enum.GetNames(typeof(TeamTypeOthers)));
2) Or should I forget creating any role specific enum TeamTypeOthers and just loop the original TeamType enum values in UI-code:
else
{
foreach (TeamType teamtype in Enum.GetValues(typeof(TeamType)))
{
if (asdf == TeamType.a)
comboboxTeamtypes.Items.Add(teamtype);
else if (asdf == TeamType.b)
comboboxTeamtypes.Items.Add(teamtype);
if (asdf == TeamType.c)
comboboxTeamtypes.Items.Add(teamtype);
}
}
I think the first solution is nice and clean (though repetitive which is not so nice). But now I also make decision about the use of enum in deeper architecture than in solution 2 which is probably bad and supports the use of solution 2. To me solution 2 is ugly and messy because I don't like loop cultivation around the code.
I would change things a little and use simple bitmasking for permissions:
public enum TeamType
{
a = 1, // or 00001
b = 2, // or 00010
c = 4, // or 00100
d = 8, // or 01000
e = 16 // or 10000
}
Then each usertype gets its permission level set (each available permission added together):
int administratorLevel = 32; // 11111
int userLevel = 7; // 00111
And then the bitmasking comes in when you populate your dropdown in the UI:
comboboxTeamTypes.Items.AddRange(
Enum.GetValues(typeof(TeamType))
.Where(v => myLevel & v == v)
.Select(v => Enum.GetName(typeof(TeamType), v));
What is your programming language? In Java, I would tell you to create enum class like below, but obviously this isn't Java.
public enum TeamType {
A(false), B(false), C(false), D(true), E(true);
private final boolean isAdmin;
public TeamType(boolean isAdmin) {
this.isAdmin = isAdmin;
}
public boolean isAdmin() {
return this.isAdmin;
}
}