C# using [Flags] with [Description] - c#

I'm looking to combine the use of enumerations - with the bitwise [Flags] - as well as using a description of the combination of the results. I've looked into examples here using the Flags Attribute, and using the Description Attribute, but not both.
Something like:
[Flags]
public enum ReasonCode
{
[Description("Verified")]
None = 0,
[Description("Check A")]
Reason1 = 1,
[Description("Check B")]
Reason2 = 2,
[Description("Check C")]
Reason3 = 4,
[Description("Check D")]
Reason4 = 8,
[Description("Check E")]
Reason5 = 16,
[Description("Check F")]
Reason6 = 32,
[Description("Check G")]
Reason7 = 64
}
I need to specify all reasons why there was a failure. Using "Reason1", etc... isn't descriptive enough for what I am looking for. I would need a much more verbose description, like "Reason1 - Check A".
For Example:
A value of 5 would be Reason1 and Reason3.
The description would then be:
Failure:
Reason1 - Check A.
Reason3 - Check C.
Is is possible to do combine descriptions like flags?

Here's a more generic extension method that I have used which will deal with all enum values including flag combinations:
public static string GetDescription(this Enum value)
{
//pull out each value in case of flag enumeration
var values = value.ToString().Split(',').Select(s => s.Trim());
var type = value.GetType();
return string.Join(" | ", values.Select(enumValue => type.GetMember(enumValue)
.FirstOrDefault()
?.GetCustomAttribute<DescriptionAttribute>()
?.Description
?? enumValue.ToString()));
}
If a description attribute is not present then it will simply return the value itself.

In order to produce the result you are looking for, use the following code:
public static String GetDescription(ReasonCode reasonCode)
{
if (reasonCode == ReasonCode.None)
return "Verified";
StringBuilder sb = new StringBuilder();
sb.AppendLine("Failed:");
foreach (ReasonCode rc in Enum.GetValues(typeof(ReasonCode)).Cast<ReasonCode>())
{
if (rc == ReasonCode.None)
continue;
if (reasonCode.HasFlag(rc))
sb.AppendLine(rc.ToString() + " - " + GetEnumDescription(rc));
}
return sb.ToString();
}
The code used to retrieve the description value is based on this implementation:
public static String GetEnumDescription(Enum value)
{
String valueText = value.ToString();
Type type = value.GetType();
FieldInfo fi = type.GetField(valueText);
Object[] attributes = fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
DescriptionAttribute attribute = (DescriptionAttribute)attributes[0];
return attribute.Description;
}
return valueText;
}
You can find a working demo here.

Related

Is there an easier way to parse an int to a generic Flags enum?

I've got a generic function to parse an object into a generic Enum.
However, I'm running into an issue when trying to safely parse an int into a [Flags] Enum.
Directly using Enum.ToObject() works to parse valid combinations, but will just return the original value if there isn't a flag combination.
Additionally, when there's no explicit enum member for a combination of flags, Enum.ToName() and Enum.IsDefined() don't return helpful values.
For Example:
[Flags]
public enum Color
{
None = 0,
Red = 1,
Green = 2,
Blue = 4,
}
// Returns 20
Enum.ToObject(typeof(Color), 20)
// Returns ""
Enum.ToName(typeof(Color), 3)
// Returns false
Enum.IsDefined(typeof(Color), 3)
I've written a function that I think technically works, but it seems like there has to be a better way to do this.
My Function:
public static T ParseEnumerator<T>(object parseVal, T defaultVal) where T : struct, IConvertible
{
Type ttype = typeof(T);
if (!ttype.IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
bool isFlag = ttype.GetCustomAttribute(typeof(FlagsAttribute)) != null;
try
{
if (parseVal == null)
{
return defaultVal;
}
else if (parseVal is T)
{
return (T)parseVal;
}
else if (parseVal is string)
{
return (T)Enum.Parse(ttype, parseVal.ToString(), true);
}
//**************** The section at issue **********************************/
else if (isFlag && parseVal is int)
{
List<string> flagsList = new List<string>();
int maxVal = 0;
//Loop through each bit flag
foreach (var val in Enum.GetValues(ttype))
{
if (CountBits((int)val) == 1)
{
if ((int)val > maxVal)
maxVal = (int)val;
// If the current bit is set, add the flag to the result
if (((int)parseVal & (int)val) == (int)val)
{
string enumName = Enum.GetName(ttype, val);
if (!string.IsNullOrEmpty(enumName))
flagsList.Add(enumName);
}
}
}
// Is the value being parsed over the highest bitwise value?
if ((int)parseVal >= (maxVal << 1))
return defaultVal;
else
return (T)Enum.Parse(ttype, string.Join(",", flagsList));
}
//************************************************************************/
else
{
string val = Enum.GetName(ttype, parseVal);
if (!string.IsNullOrEmpty(val))
return (T)Enum.ToObject(ttype, parseVal);
else
return defaultVal;
}
}
catch
{
return defaultVal;
}
}
Is there something I'm missing? Or is there another way to parse these values safely?
Any help is appreciated, thanks!
-MM
Since your generic function has to know the Enum type to begin with, you can just scrap the function and use basic casting instead.
using System;
namespace SO_58455415_enum_parsing {
[Flags]
public enum CheeseCharacteristics {
Yellow = 1,
Mouldy = 2,
Soft = 4,
Holed = 8
}
public static class Program {
static void Main(string[] args) {
CheeseCharacteristics cc = (CheeseCharacteristics)12;
Console.WriteLine(cc);
}
}
}
If all you want to know is if you have a value that can be created using the enum flags.. that's pretty easy, as long as we can assume that each flag is "sequential" (e.g. there are no gaps between the flags). All numbers between 1 and the sum of all flag values can be made by some combination of flags. You can simply sum the flag values together and compare that to your question value.
public static bool IsValidFlags<T>(int checkValue) where T:Enum {
int maxFlagValue = ((int[])Enum.GetValues(typeof(T))).Sum();
return (0 < checkValue) && (checkValue <= maxFlagValue);
}
For future reference, you can constrain your generic parameter to an enum:
void fx<T> () where T:Enum { }

Serialize C# Enum Definition to Json

Given the following in C#:
[Flags]
public enum MyFlags {
None = 0,
First = 1 << 0,
Second = 1 << 1,
Third = 1 << 2,
Fourth = 1 << 3
}
Are there any existing methods in ServiceStack.Text for serializing to the following JSON?
{
"MyFlags": {
"None": 0,
"First": 1,
"Second": 2,
"Third": 4,
"Fourth": 8
}
}
Currently I'm using the routine below, are there better ways to do this?
public static string ToJson(this Type type)
{
var stringBuilder = new StringBuilder();
Array values = Enum.GetValues(type);
stringBuilder.Append(string.Format(#"{{ ""{0}"": {{", type.Name));
foreach (Enum value in values)
{
stringBuilder.Append(
string.Format(
#"""{0}"": {1},",
Enum.GetName(typeof(Highlights), value),
Convert.ChangeType(value, value.GetTypeCode())));
}
stringBuilder.Remove(stringBuilder.Length - 1, 1);
stringBuilder.Append("}}");
return stringBuilder.ToString();
}
public static class EnumExtensions
{
public static string EnumToJson(this Type type)
{
if (!type.IsEnum)
throw new InvalidOperationException("enum expected");
var results =
Enum.GetValues(type).Cast<object>()
.ToDictionary(enumValue => enumValue.ToString(), enumValue => (int) enumValue);
return string.Format("{{ \"{0}\" : {1} }}", type.Name, Newtonsoft.Json.JsonConvert.SerializeObject(results));
}
}
Using a dictionary of to do the heavy lifting. Then using Newtonsoft's json convert to convert that to json. I just had to do a bit of wrapping to add the type name on.
You're better off populating a Dictionary<string,int> or a Typed DTO and serializing that.

How to get the DescriptionAttribute value from an enum member

I have a enum type
public enum DataType:int
{
None = 0,
[Description("A")]
Alpha = 1,
[Description("N")]
Numeric,
[Description("AN")]
AlphaNumeric,
[Description("D")]
Date
}
How do I retrieve the description attribute value of, say, Alpha.
Eg(ideal) : DataType.Alpha.Attribute should give "A"
Use this
private string GetEnumDescription(Enum value)
{
// Get the Description attribute value for the enum value
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
I have an extension method to do just that:
public static string GetDescription(this Enum enumValue)
{
//Look for DescriptionAttributes on the enum field
object[] attr = enumValue.GetType().GetField(enumValue.ToString())
.GetCustomAttributes(typeof (DescriptionAttribute), false);
if (attr.Length > 0) // a DescriptionAttribute exists; use it
return ((DescriptionAttribute) attr[0]).Description;
//The above code is all you need if you always use DescriptionAttributes;
//If you don't, the below code will semi-intelligently
//"humanize" an UpperCamelCased Enum identifier
string result = enumValue.ToString();
//"FooBar" -> "Foo Bar"
result = Regex.Replace(result, #"([a-z])([A-Z])", "$1 $2");
//"Foo123" -> "Foo 123"
result = Regex.Replace(result, #"([A-Za-z])([0-9])", "$1 $2");
//"123Foo" -> "123 Foo"
result = Regex.Replace(result, #"([0-9])([A-Za-z])", "$1 $2");
//"FOOBar" -> "FOO Bar"
result = Regex.Replace(result, #"(?<!^)(?<! )([A-Z][a-z])", " $1");
return result;
}
Usage:
var description = DataType.Alpha.GetDescription(); //"A"
public enum TestEnums
{
IAmAComplexABCEnumValue,
}
//"I Am A Complex ABC Enum Value"
var complexCamelCasedDescription = TestEnums.IAmAComplexABCEnumValue.GetDescription();
I've created a custom generic method to get any attribute value from an enum.
public static class EnumHelper
{
public static string GetAttributeValueByEnumValue<EnumType, AttributeType>(EnumType value, string attributeProperty = null)
where EnumType : Enum
where AttributeType : Attribute
{
var enumField = value.GetType().GetField(value.ToString());
var enumAttributes = enumField?.GetCustomAttributes(typeof(AttributeType), false) as AttributeType[];
if (enumAttributes == null || !enumAttributes.Any())
throw new Exception($"Attribute [{typeof(AttributeType).Name}] not found in enum [{typeof(EnumType).Name}] type with value [{value.ToString()}]!");
var enumAttribute = enumAttributes.First();
var attributeValue = enumAttribute
.GetType()
?.GetProperty(!string.IsNullOrEmpty(attributeProperty) ? attributeProperty : typeof(AttributeType).Name.Replace("Attribute", string.Empty))
?.GetValue(enumAttribute);
if (attributeValue == null)
throw new Exception("Error reading enum attribute value!");
return attributeValue.ToString();
}
}
To use it, simply call it like below:
EnumHelper.GetAttributeValueByEnumValue<EnumLookup, DescriptionAttribute>(enumValue);
You can also provide the attributeProperty name in case of a custom enum, like below:
EnumHelper.GetAttributeValueByEnumValue<EnumLookup, TitleAttribute>(enumValue, "TitleName");
I hope it helps you :)

How do I pass in an Enum as a parameter such that it can be used to perform a cast?

In the stub below how do I pass in (MyEnum) as a parameter such that I can use this procedure with any enum?
public static Enum Proc(this Enum e)
{
Int32 i = (Int32)(MyEnum)e;
...
Here is the solution I have come up with that works:
public static Enum Next(this Enum e, Type eT)
{
Int32 i = (Int32)(Object)e;
return (Enum)Enum.Parse(eT, Enum.GetName(eT, Enum.GetName(eT, ++i) == null? i = 0 : i));
}
This solution isn't ideal because I have to do this to get the next value:
MyEnum e = (MyEnum)e.Next(typeof(MyEnum));
I'd rather just do
MyEnum e = e.Next(typeof(MyEnum));
Or even better:
MyEnum e = e.Next();
Anyone who can provide the simple solution can have the point.
Also the code I've written above runs fine in LinqPad but only compiles in WP7 and then throws an exception when I run it (InvalidProgramException).
Here's a function that will cycle through the values of any enum:
static public Enum Cycle(this Enum e)
{
bool found = false;
Enum first = null;
foreach (Enum i in Enum.GetValues(e.GetType()))
{
if (first == null)
first = i;
if (found)
return i;
found = e.Equals(i);
}
if (found)
return first;
return null;
}
For versions of C# that don't have Enum.GetValues, you'll have to use a method like this which only works for enums whose values start at 0 and increment by 1:
static public Enum Cycle(this Enum e)
{
var i = ((IConvertible)e).ToInt64(null) + 1;
var eT = e.GetType();
var next = Enum.GetName(eT, i);
return (Enum)Enum.Parse(eT, next ?? Enum.GetName(eT, 0), false);
}
Use it like:
var nextEnum = (MyEnum)curEnum.Cycle();
Edit: I updated this to return the next enum in the list, strongly typed, regardless of what the numbering of the enum values may be. I'm able to compile and run this under .NET 4 and haven't tried it on WP7, but I don't think I'm using anything that's missing/disabled in SL/WP7.
public static T Next<T>(this T e) where T : struct
{
var t = typeof(T);
if (!t.IsEnum) throw new ArgumentException("T must be an enumerated type");
if (!Enum.IsDefined(t, e)) throw new ArgumentException();
var intValue = (int)t.GetField(e.ToString()).GetValue(null);
var enumValues = t.GetFields(BindingFlags.Public | BindingFlags.Static).Select(x => x.GetValue(null));
var next = (T?)enumValues.Where(x => (int)x > intValue).Min();
if (next.HasValue)
return next.Value;
else
return (T)enumValues.Min();
}
It can be used as simply as:
var nextE = e.Next();

C# Iterating through an enum? (Indexing a System.Array)

I have the following code:
// Obtain the string names of all the elements within myEnum
String[] names = Enum.GetNames( typeof( myEnum ) );
// Obtain the values of all the elements within myEnum
Array values = Enum.GetValues( typeof( myEnum ) );
// Print the names and values to file
for ( int i = 0; i < names.Length; i++ )
{
print( names[i], values[i] );
}
However, I cannot index values. Is there an easier way to do this?
Or have I missed something entirely!
Array values = Enum.GetValues(typeof(myEnum));
foreach( MyEnum val in values )
{
Console.WriteLine (String.Format("{0}: {1}", Enum.GetName(typeof(MyEnum), val), val));
}
Or, you can cast the System.Array that is returned:
string[] names = Enum.GetNames(typeof(MyEnum));
MyEnum[] values = (MyEnum[])Enum.GetValues(typeof(MyEnum));
for( int i = 0; i < names.Length; i++ )
{
print(names[i], values[i]);
}
But, can you be sure that GetValues returns the values in the same order as GetNames returns the names ?
You need to cast the array - the returned array is actually of the requested type, i.e. myEnum[] if you ask for typeof(myEnum):
myEnum[] values = (myEnum[]) Enum.GetValues(typeof(myEnum));
Then values[0] etc
You can cast that Array to different types of Arrays:
myEnum[] values = (myEnum[])Enum.GetValues(typeof(myEnum));
or if you want the integer values:
int[] values = (int[])Enum.GetValues(typeof(myEnum));
You can iterate those casted arrays of course :)
How about a dictionary list?
Dictionary<string, int> list = new Dictionary<string, int>();
foreach( var item in Enum.GetNames(typeof(MyEnum)) )
{
list.Add(item, (int)Enum.Parse(typeof(MyEnum), item));
}
and of course you can change the dictionary value type to whatever your enum values are.
What about using a foreach loop, maybe you could work with that?
int i = 0;
foreach (var o in values)
{
print(names[i], o);
i++;
}
something like that perhaps?
Here is another. We had a need to provide friendly names for our EnumValues. We used the System.ComponentModel.DescriptionAttribute to show a custom string value for each enum value.
public static class StaticClass
{
public static string GetEnumDescription(Enum currentEnum)
{
string description = String.Empty;
DescriptionAttribute da;
FieldInfo fi = currentEnum.GetType().
GetField(currentEnum.ToString());
da = (DescriptionAttribute)Attribute.GetCustomAttribute(fi,
typeof(DescriptionAttribute));
if (da != null)
description = da.Description;
else
description = currentEnum.ToString();
return description;
}
public static List<string> GetEnumFormattedNames<TEnum>()
{
var enumType = typeof(TEnum);
if (enumType == typeof(Enum))
throw new ArgumentException("typeof(TEnum) == System.Enum", "TEnum");
if (!(enumType.IsEnum))
throw new ArgumentException(String.Format("typeof({0}).IsEnum == false", enumType), "TEnum");
List<string> formattedNames = new List<string>();
var list = Enum.GetValues(enumType).OfType<TEnum>().ToList<TEnum>();
foreach (TEnum item in list)
{
formattedNames.Add(GetEnumDescription(item as Enum));
}
return formattedNames;
}
}
In Use
public enum TestEnum
{
[Description("Something 1")]
Dr = 0,
[Description("Something 2")]
Mr = 1
}
static void Main(string[] args)
{
var vals = StaticClass.GetEnumFormattedNames<TestEnum>();
}
This will end returning "Something 1", "Something 2"
Another solution, with interesting possibilities:
enum Days { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }
static class Helpers
{
public static IEnumerable<Days> AllDays(Days First)
{
if (First == Days.Monday)
{
yield return Days.Monday;
yield return Days.Tuesday;
yield return Days.Wednesday;
yield return Days.Thursday;
yield return Days.Friday;
yield return Days.Saturday;
yield return Days.Sunday;
}
if (First == Days.Saturday)
{
yield return Days.Saturday;
yield return Days.Sunday;
yield return Days.Monday;
yield return Days.Tuesday;
yield return Days.Wednesday;
yield return Days.Thursday;
yield return Days.Friday;
}
}
Old question, but a slightly cleaner approach using LINQ's .Cast<>()
var values = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>();
foreach(var val in values)
{
Console.WriteLine("Member: {0}",val.ToString());
}
In the Enum.GetValues results, casting to int produces the numeric value. Using ToString() produces the friendly name. No other calls to Enum.GetName are needed.
public enum MyEnum
{
FirstWord,
SecondWord,
Another = 5
};
// later in some method
StringBuilder sb = new StringBuilder();
foreach (var val in Enum.GetValues(typeof(MyEnum))) {
int numberValue = (int)val;
string friendyName = val.ToString();
sb.Append("Enum number " + numberValue + " has the name " + friendyName + "\n");
}
File.WriteAllText(#"C:\temp\myfile.txt", sb.ToString());
// Produces the output file contents:
/*
Enum number 0 has the name FirstWord
Enum number 1 has the name SecondWord
Enum number 5 has the name Another
*/
Array has a GetValue(Int32) method which you can use to retrieve the value at a specified index.
Array.GetValue
You can simplify this using format strings. I use the following snippet in usage messages:
writer.WriteLine("Exit codes are a combination of the following:");
foreach (ExitCodes value in Enum.GetValues(typeof(ExitCodes)))
{
writer.WriteLine(" {0,4:D}: {0:G}", value);
}
The D format specifier formats the enum value as a decimal. There's also an X specifier that gives hexadecimal output.
The G specifier formats an enum as a string. If the Flags attribute is applied to the enum then combined values are supported as well. There's an F specifier that acts as if Flags is always present.
See Enum.Format().
Here is a simple way to iterate through your custom Enum object
For Each enumValue As Integer In [Enum].GetValues(GetType(MyEnum))
Print([Enum].GetName(GetType(MyEnum), enumValue).ToString)
Next
Ancient question, but 3Dave's answer supplied the easiest approach. I needed a little helper method to generate a Sql script to decode an enum value in the database for debugging. It worked great:
public static string EnumToCheater<T>() {
var sql = "";
foreach (var enumValue in Enum.GetValues(typeof(T)))
sql += $#"when {(int) enumValue} then '{enumValue}' ";
return $#"case ?? {sql}else '??' end,";
}
I have it in a static method, so usage is:
var cheater = MyStaticClass.EnumToCheater<MyEnum>()

Categories