Enum to list as an extension? - c#

I have various enums that I use as sources for dropdown lists, In order to provide for a user-friendly description, I added a Description attribute to each enum, and then do the following:
var list = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
The above is repetitive because I have to use it in a lot of places. I tried to add an extension method:
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
public static KeyValuePair<T, string> ToList<T>(this Enum source)
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
}
However, I get an exception:
Cannot convert lambda expression to type 'System.Collections.Generic.IEqualityComparer' because it is not a delegate type
What is the correct way to use it as an extension (using the above 2 methods)?

What is the correct way to use it as an extension (using the above 2 methods)?
There is no correct way to use it as an extension. Extension methods (similar to instance methods) are used when you have a value (instance) and for instance want to get some information related to that value. So the extension method would make sense if you want to get the description of a single enum value.
However, in your case the information you need (the list of enum value/description pairs) is not tied to a specific enum value, but to the enum type. Which means you just need a plain static generic method similar to Enum.TryParse<TEnum>. Ideally you would constrain the generic argument to allow only enum, but this type of constraint is not supported (yet), so we'll use (similar to the above system method) just where TEnum : struct and will add runtime check.
So here is a sample implementation:
public static class EnumInfo
{
public static List<KeyValuePair<TEnum, string>> GetList<TEnum>()
where TEnum : struct
{
if (!typeof(TEnum).IsEnum) throw new InvalidOperationException();
return ((TEnum[])Enum.GetValues(typeof(TEnum)))
.ToDictionary(k => k, v => ((Enum)(object)v).GetAttributeOfType<DescriptionAttribute>().Description)
.ToList();
}
}
and usage:
public enum MyEnum
{
[Description("Foo")]
A,
[Description("Bar")]
B,
[Description("Baz")]
C,
}
var list = EnumInfo.GetList<MyEnum>();

I have this extension method in my stack and use it for the same thing all the time.
public static string Description(this Enum #enum)
{
try
{
var #string = #enum.ToString();
var attribute =
#enum.GetType()
.GetField(#string)
.GetCustomAttribute<DescriptionAttribute>(false);
return attribute != null ? attribute.Description : #string;
}
catch // Log nothing, just return an empty string
{
return string.Empty;
}
}
Example usage:
MyEnum.Value.Description(); // The value from within the description attr.
Additionally, you can use this one to get a IDictionary for binding purposes.
public static IDictionary<string, string> ToDictionary(this Type type)
{
if (!type.IsEnum)
{
throw new InvalidCastException("'enumValue' is not an Enumeration!");
}
var names = Enum.GetNames(type);
var values = Enum.GetValues(type);
return Enumerable.Range(0, names.Length)
.Select(index => new
{
Key = names[index],
Value = ((Enum)values.GetValue(index)).Description()
})
.ToDictionary(k => k.Key, k => k.Value);
}
Use it like so:
var dictionary = typeof(MyEnum).ToDictionary();
Update
Here is a working .NET Fiddle.
public static Dictionary<TEnum, string> ToDictionary<TEnum>(this Type type)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
return Enum.GetValues(type)
.OfType<TEnum>()
.ToDictionary(value => value, value => value.Description());
}
Then use it like this:
public enum Test
{
[Description("A test enum value for 'Foo'")]
Foo,
[Description("A test enum value for 'Bar'")]
Bar
}
typeof(Test).ToDictionary<Test>()

You can create a generic method which would take Enum and Attribute as generic argument.
For getting any attribute, you can create an extension method like:
public static string AttributeValue<TEnum,TAttribute>(this TEnum value,Func<TAttribute,string> func) where T : Attribute
{
FieldInfo field = value.GetType().GetField(value.ToString());
T attribute = Attribute.GetCustomAttribute(field, typeof(T)) as T;
return attribute == null ? value.ToString() : func(attribute);
}
and here is the method for converting it to dictionary:
public static Dictionary<TEnum,string> ToDictionary<TEnum,TAttribute>(this TEnum obj,Func<TAttribute,string> func)
where TEnum : struct, IComparable, IFormattable, IConvertible
where TAttribute : Attribute
{
return (Enum.GetValues(typeof(TEnum)).OfType<TEnum>()
.Select(x =>
new
{
Value = x,
Description = x.AttributeValue<TEnum,TAttribute>(func)
}).ToDictionary(x=>x.Value,x=>x.Description));
}
You can call it this way:
var test = eUserRole.SuperAdmin
.ToDictionary<eUserRole,EnumDisplayNameAttribute>(attr=>attr.DisplayName);
I have used this Enum and Attribute as example:
public class EnumDisplayNameAttribute : Attribute
{
private string _displayName;
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
}
public enum eUserRole : int
{
[EnumDisplayName(DisplayName = "Super Admin")]
SuperAdmin = 0,
[EnumDisplayName(DisplayName = "Phoenix Admin")]
PhoenixAdmin = 1,
[EnumDisplayName(DisplayName = "Office Admin")]
OfficeAdmin = 2,
[EnumDisplayName(DisplayName = "Report User")]
ReportUser = 3,
[EnumDisplayName(DisplayName = "Billing User")]
BillingUser = 4
}
Output:

Another take on this:
class Program
{
//Example enum
public enum eFancyEnum
{
[Description("Obsolete")]
Yahoo,
[Description("I want food")]
Meow,
[Description("I want attention")]
Woof,
}
static void Main(string[] args)
{
//This is how you use it
Dictionary<eFancyEnum, string> myDictionary = typeof(eFancyEnum).ToDictionary<eFancyEnum>();
}
}
public static class EnumExtension
{
//Helper method to get description
public static string ToDescription<T>(this T en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return en.ToString();
}
//The actual extension method that builds your dictionary
public static Dictionary<T, string> ToDictionary<T>(this Type source) where T : struct, IConvertible
{
if(!source.IsEnum || typeof(T) != source)
{
throw new InvalidEnumArgumentException("BOOM");
}
Dictionary<T, string> retVal = new Dictionary<T,string>();
foreach (var item in Enum.GetValues(typeof(T)).Cast<T>())
{
retVal.Add(item, item.ToDescription());
}
return retVal;
}
}

Whenever I need an enumeration (a static list of known values) that need to have something more than just a mere integer value and a string counterpart, I end up using this Enumeration Utility class that essentially gives me java-like enumeration behavior.
So that would be my first option if I were on op's shoes as it would make it really trivial to achieve what he/she wants.
But, assuming this is not an option for op and she/he need to stick with C# enums, I would use a combination of both ehsan-sajjad and frank-j solutions:
Have an extension method to return the description of a given enum
item, which is pretty much what op had already;
Have a static helper method to return a dictionary of items and their respective descriptions for a given enum type.
Here is how I would implement this:
public static class EnumUtils
{
public static string GetDescription(this Enum enumVal)
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof (DescriptionAttribute), false);
return (attributes.Length > 0) ? ((DescriptionAttribute) attributes[0]).Description : null;
}
public static Dictionary<TEnum, string> GetItemsWithDescrition<TEnum>()
{
var enumType = typeof(TEnum);
if (!enumType.IsEnum)
{
throw new InvalidOperationException("TEnum must be an enum type");
}
return Enum
.GetValues(enumType)
.Cast<TEnum>()
.ToDictionary(enumValue => enumValue, enumValue => GetDescription(enumValue as Enum));
}
}
And here is what the usage would look like:
public class EnumUtilsTests
{
public enum MyEnum
{
[Description("Um")]
One,
[Description("Dois")]
Two,
[Description("Tres")]
Three,
NoDescription
}
public void Should_get_enum_description()
{
MyEnum.One.GetDescription().ShouldBe("Um");
MyEnum.Two.GetDescription().ShouldBe("Dois");
MyEnum.Three.GetDescription().ShouldBe("Tres");
MyEnum.NoDescription.GetDescription().ShouldBe(null);
}
public void Should_get_all_enum_values_with_description()
{
var response = EnumUtils.GetItemsWithDescrition<MyEnum>();
response.ShouldContain(x => x.Key == MyEnum.One && x.Value == "Um");
response.ShouldContain(x => x.Key == MyEnum.Two && x.Value == "Dois");
response.ShouldContain(x => x.Key == MyEnum.Three && x.Value == "Tres");
response.ShouldContain(x => x.Key == MyEnum.NoDescription && x.Value == null);
}
}

Try replacing
.ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
with
.Select(t => new { k = t, v = t.GetAttributeOfType<DescriptionAttribute>().Description)
.ToDictionary(s => s.k, s => s.v)
In your example, the wrong overload of ToDictionary() is being called.

Related

Generics, enums and custom attributes - is it possible?

Apologies for the amount of code, but it is easier to explain it this way.
I have a custom attribute CustomUserData implemented as follows:
public class CustomUserData : Attribute
{
public CustomUserData(object aUserData)
{
UserData = aUserData;
}
public object UserData { get; set; }
}
and an extension method for enums as:
public static class EnumExtensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum aValue) where TAttribute : Attribute
{
Type type = aValue.GetType();
string name = Enum.GetName(type, aValue);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
public static object GetCustomUserData(this Enum aValue)
{
CustomUserData userValue = GetAttribute<CustomUserData>(aValue);
return userValue != null ? userValue.UserData : null;
}
}
I then have a helper class that serializes/deserializes an enum that has custom data associated with it as follows:
public static class ParameterDisplayModeEnumListHelper
{
public static List<ParameterDisplayModeEnum> FromDatabase(string aDisplayModeString)
{
//Default behaviour
List<ParameterDisplayModeEnum> result = new List<ParameterDisplayModeEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (ParameterDisplayModeEnum displayModeEnum in Enum.GetValues(typeof (ParameterDisplayModeEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add(displayModeEnum);
}
}
return result;
}
public static string ToDatabase(List<ParameterDisplayModeEnum> aDisplayModeList)
{
string result = string.Empty;
foreach (ParameterDisplayModeEnum listItem in aDisplayModeList)
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
however this is specific to ParameterDisplayModeEnum and I have a bunch of enums that I need to treat this way for serialization/deserialization so I would prefer to have a generic such as:
public static class EnumListHelper<TEnum>
{
public static List<TEnum> FromDatabase(string aDisplayModeString)
{
//Default behaviour
List<TEnum> result = new List<TEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (TEnum displayModeEnum in Enum.GetValues(typeof (TEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add(displayModeEnum);
}
}
return result;
}
public static string ToDatabase(List<TEnum> aDisplayModeList)
{
string result = string.Empty;
foreach (TEnum listItem in aDisplayModeList)
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
However this will not work as GetCustomUserData() cannot be invoked. Any suggestions? I cannot change the use of the custom attribute or the use of the enums. I am looking for a generic way to do the serialization/deserialization without having to write a concrete list helper class each time.
All suggestions appreciated.
Try this code:
public static class EnumListHelper
{
private static void EnsureIsEnum<TEnum>()
{
if (!typeof(TEnum).IsEnum)
throw new InvalidOperationException(string.Format("The {0} type is not an enum.", typeof(TEnum)));
}
public static List<TEnum> FromDatabase<TEnum>(string aDisplayModeString)
where TEnum : struct
{
EnsureIsEnum<TEnum>();
//Default behaviour
List<TEnum> result = new List<TEnum>();
//Split the string list into a list of strings
List<string> listOfDisplayModes = new List<string>(aDisplayModeString.Split(','));
//Iterate the enum looking for matches in the list
foreach (Enum displayModeEnum in Enum.GetValues(typeof(TEnum)))
{
if (listOfDisplayModes.FindIndex(item => item == (string)displayModeEnum.GetCustomUserData()) >= 0)
{
result.Add((TEnum)(object)displayModeEnum);
}
}
return result;
}
public static string ToDatabase<TEnum>(List<TEnum> aDisplayModeList)
where TEnum : struct
{
EnsureIsEnum<TEnum>();
string result = string.Empty;
foreach (var listItem in aDisplayModeList.OfType<Enum>())
{
if (result != string.Empty)
result += ",";
result += listItem.GetCustomUserData();
}
return result;
}
}
var fromDatabase = EnumListHelper.FromDatabase<TestEnum>("test");
EnumListHelper.ToDatabase(fromDatabase);
UPDATE 0
To be clear, because we cannot restrict generics to Enum we should check that the type TEnum is an enum and throw an exception if it is not.
When we use the FromDatabase method we know that TEnum is enum, and we can write this code to cast an enum to the specified TEnum:
result.Add((TEnum)(object)displayModeEnum)
in the ToDatabase method we also know that TEnum is enum and we can write this code to convert TEnum to the Enum type:
aDisplayModeList.OfType<Enum>()
Ideally you'd want to restrict TEnum to Enum but that won't work as you can not restrict generics to Enum MicrosoftBut try following, it might do the trick...
if (listOfDisplayModes.FindIndex(item =>
item == (string)(displayModeEnum as Enum).GetCustomUserData()) >= 0)

Generic Enum to SelectList extension method

I need to create a SelectList from any Enum in my project.
I have the code below which I create a select list from a specific enum, but I'd like to make an extension method for ANY enum. This example retrieves the value of the DescriptionAttribute on each Enum value
var list = new SelectList(
Enum.GetValues(typeof(eChargeType))
.Cast<eChargeType>()
.Select(n => new
{
id = (int)n,
label = n.ToString()
}), "id", "label", charge.type_id);
Referencing this post, how do I proceed?
public static void ToSelectList(this Enum e)
{
// code here
}
What I think you are struggling with, is the retrieval of the description. I'm sure once you have those that you can define your final method which gives your exact result.
First, if you define an extension method, it works on a value of the enum, not on the enum type itself. And I think, for easy of usage, you would like to call the method on the type (like a static method). Unfortunately, you cannot define those.
What you can do is the following. First define a method which retrieves the description of the enum value, if it has one:
public static string GetDescription(this Enum value) {
string description = value.ToString();
FieldInfo fieldInfo = value.GetType().GetField(description);
DescriptionAttribute[] attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) {
description = attributes[0].Description;
}
return description;
}
Next, define a method which takes all values of an enum, and use the previous method to look up the value which we want to show, and return that list. The generic argument can be inferred.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>(this TEnum value) {
return Enum
.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(x => new KeyValuePair<TEnum, string>(x, ((Enum)((object)x)).GetDescription()))
.ToList();
}
And finally, for ease of usage, a method to call it directly without value. But then the generic argument is not optional.
public static List<KeyValuePair<TEnum, string>> ToEnumDescriptionsList<TEnum>() {
return ToEnumDescriptionsList<TEnum>(default(TEnum));
}
Now we can use it like this:
enum TestEnum {
[Description("My first value")]
Value1,
Value2,
[Description("Last one")]
Value99
}
var items = default(TestEnum).ToEnumDescriptionsList();
// or: TestEnum.Value1.ToEnumDescriptionsList();
// Alternative: EnumExtensions.ToEnumDescriptionsList<TestEnum>()
foreach (var item in items) {
Console.WriteLine("{0} - {1}", item.Key, item.Value);
}
Console.ReadLine();
Which outputs:
Value1 - My first value
Value2 - Value2
Value99 - Last one
Late to the party, but since there is no accepted answer and it might help others:
As #Maarten mentioned, an extension method works on the value of an enum, not the enum type itelf, so as with Maarteen's soultion you can create a dummy or default value to call the extension method on, however, you may find, as I did, that it is simpler to just use a static helper method like so:
public static class EnumHelper
{
public static SelectList GetSelectList<T>(string selectedValue, bool useNumeric = false)
{
Type enumType = GetBaseType(typeof(T));
if (enumType.IsEnum)
{
var list = new List<SelectListItem>();
// Add empty option
list.Add(new SelectListItem { Value = string.Empty, Text = string.Empty });
foreach (Enum e in Enum.GetValues(enumType))
{
list.Add(new SelectListItem { Value = useNumeric ? Convert.ToInt32(e).ToString() : e.ToString(), Text = e.Description() });
}
return new SelectList(list, "Value", "Text", selectedValue);
}
return null;
}
private static bool IsTypeNullable(Type type)
{
return (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
private static Type GetBaseType(Type type)
{
return IsTypeNullable(type) ? type.GetGenericArguments()[0] : type;
}
You would create the select list like this:
viewModel.ProvinceSelect = EnumHelper.GetSelectList<Province>(model.Province);
or using the optional numeric values instead of strings:
viewModel.MonthSelect = EnumHelper.GetSelectList<Month>(model.Month,true);
The basic idea for this I got from here, though I changed it to suit my needs. One thing I added was the ability to optionally use ints for the value. I also added an enum extension to get the description attribute which is based on this blog post:
public static class EnumExtensions
{
public static string Description(this Enum en)
{
Type type = en.GetType();
MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attrs != null && attrs.Length > 0)
{
return ((DescriptionAttribute)attrs[0]).Description;
}
}
return en.ToString();
}
}
Since enum can't have extensions pinned to the entire collection a convenient way to extend the Enum base class is with a static typed class. This will allow concise code such as:
Enum<MyCustomEnumType>.GetSelectItems();
Which can be achieved with the following code:
public static class Enum<T>
{
public static SelectListItem[] GetSelectItems()
{
Type type = typeof(T);
return
Enum.GetValues(type)
.Cast<object>()
.Select(v => new SelectListItem() { Value = v.ToString(), Text = Enum.GetName(type, v) })
.ToArray();
}
}
Since enum do not have a shared interface Type misuse is possible, but the class name Enum should dispell any confusion.
Here is a corrected [type casted value to int] and simplified [uses tostring override instead of getname] version of Nathaniels answer that returns a List instead of an array:
public static class Enum<T>
{
//usage: var lst = Enum<myenum>.GetSelectList();
public static List<SelectListItem> GetSelectList()
{
return Enum.GetValues( typeof(T) )
.Cast<object>()
.Select(i => new SelectListItem()
{ Value = ((int)i).ToString()
,Text = i.ToString() })
.ToList();
}
}

How to get all descriptions of enum values with reflection?

So I need to get a List<string> from my enum
Here is what I have done so far:
enum definition
[Flags]
public enum ContractorType
{
[Description("Recipient")]
RECIPIENT = 1,
[Description("Deliver")]
DELIVER = 2,
[Description("Recipient / Deliver")]
RECIPIENT_DELIVER = 4
}
HelperClass with method to do what I need:
public static class EnumUtils
{
public static IEnumerable<string> GetDescrptions(Type enumerator)
{
FieldInfo[] fi = enumerator.GetFields();
List<DescriptionAttribute> attributes = new List<DescriptionAttribute>();
foreach (var i in fi)
{
try
{
yield return attributes.Add(((DescriptionAttribute[])i.GetCustomAttributes(
typeof(DescriptionAttribute),
false))[0]);
}
catch { }
}
return new List<string>{"empty"};
}
}
Now in the line where I yield values, I got a NullReferenceException. Did I miss something? The syntax looks all right to me, but maybe I overlooked something?
Edit:
I'm using .net Framework 4.0 here.
This generic static method works fine for getting a list of descriptions for each value of an enum type of T:
public static IEnumerable<string> GetDescriptions<T>()
{
var attributes = typeof(T).GetMembers()
.SelectMany(member => member.GetCustomAttributes(typeof (DescriptionAttribute), true).Cast<DescriptionAttribute>())
.ToList();
return attributes.Select(x => x.Description);
}
I created these extension methods
public static class EnumExtender
{
public static string GetDescription(this Enum enumValue)
{
string output = null;
Type type = enumValue.GetType();
FieldInfo fi = type.GetField(enumValue.ToString());
var attrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
if (attrs.Length > 0) output = attrs[0].Description;
return output;
}
public static IDictionary<T, string> GetEnumValuesWithDescription<T>(this Type type) where T : struct, IConvertible
{
if (!type.IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
return type.GetEnumValues()
.OfType<T>()
.ToDictionary(
key => key,
val => (val as Enum).GetDescription()
);
}
}
Usage
var stuff = typeof(TestEnum).GetEnumValuesWithDescription<TestEnum>();
Will return a Dictionary<TestEnum, string> with value as keys and descriptions as values. If you want just a list, you can change .ToDictionary to
.Select(o => (o as Enum).GetDescription())
.ToList()
Here is a small reusable solution. This is an abstract class which will extract all the attributes of type K from type T.
abstract class AbstractAttributes<T, K>
{
protected List<K> Attributes = new List<K>();
public AbstractAttributes()
{
foreach (var member in typeof(T).GetMembers())
{
foreach (K attribute in member.GetCustomAttributes(typeof(K), true))
Attributes.Add(attribute);
}
}
}
Should we now want to extract only attributes of DescriptionAttribute type, we would use the following class.
class DescriptionAttributes<T> : AbstractAttributes<T, DescriptionAttribute>
{
public List<string> Descriptions { get; set; }
public DescriptionAttributes()
{
Descriptions = Attributes.Select(x => x.Description).ToList();
}
}
This class will extract only attributes of DescriptionAttribute type from the type T. But to actually use this class in you context you will simply need to do the following.
new DescriptionAttributes<ContractorType>().Descriptions.ForEach(x => Console.WriteLine(x));
This line of code will write out all the descriptions you used as parameters in your attributes of type DescriptionAttribute. Should you need to extract some other attributes, just create a new class that derives from the AbstractAttributes<T, K> class and close its type K with the appropriate attribute.
You need to find the DescriptionAttribute on each field, if it exists and then retrieve the Description attribute e.g.
return enumType.GetFields()
.Select(f => (DescriptionAttribute)f.GetCustomAttribute(typeof(DescriptionAttribute)))
.Where(a => a != null)
.Select(a => a.Description)
If you could have multiple descriptions on a field, you could do something like:
FieldInfo[] fields = enumType.GetFields();
foreach(FieldInfo field in fields)
{
var descriptionAttributes = field.GetCustomAttributes(false).OfType<DescriptionAttribute>();
foreach(var descAttr in descriptionAttributes)
{
yield return descAttr.Description;
}
}
which is more similar to your existing approach.
It think this can solve your problem. If it is not implemented you can return null or an exception. It depends what you need.
public DescriptionAttribute GetDescription(ContractorType contractorType)
{
MemberInfo memberInfo = typeof(ContractorType).GetMember(contractorType.ToString())
.FirstOrDefault();
if (memberInfo != null)
{
DescriptionAttribute attribute = (DescriptionAttribute)
memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false)
.FirstOrDefault();
return attribute;
}
//return null;
//or
throw new NotImplementedException("There is no description for this enum");
}
So you will use it like this :
DescriptionAttribute attribute = GetDescription(ContractorType.RECIPIENT);
Sorry that I didn't read your question. Here is some code that you can use to take all of the description strings:
public IEnumerable<string> GetAllDescriptionInText()
{
List<string> descList = new List<string>();
foreach (DescriptionAttribute desc in Enum.GetValues(typeof(DescriptionAttribute)))
{
descList.Add(GetDescription(desc).Value);
}
return descList;
}
You can try this
public string ContractorTypeDescription(Enum ContractorType)
{
FieldInfo fi = ContractorType.GetType().GetField(ContractorType.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return ContractorType.ToString();
}
}
This is Dictionary not List
But is is something I use
using System.ComponentModel;
using System.Reflection;
using MyExtensions;
namespace MyExtensions
{
public static class Extension
{
public static string GetDescriptionName(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name == null)
return null;
else
{
FieldInfo field = type.GetField(name);
if (field == null)
return name;
else
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr == null)
return name;
else
return attr.Description;
}
}
}
}
}
namespace EnumDescription
{
class Program
{
public enum enumDateCond : byte
{
[Description("Empty")]
Null = 0,
[Description("Not Empty")]
NotNull = 1,
EQ = 2,
LT = 3,
LE = 4,
GE = 14,
GT = 15
};
static void Main(string[] args)
{
enumDateCond x = enumDateCond.Null;
string description = x.GetDescriptionName();
foreach (enumDateCond enm in Enum.GetValues(typeof(enumDateCond)))
{
description = enm.GetDescriptionName();
Console.WriteLine(description);
}
Console.WriteLine("Dictionary");
Dictionary<enumDateCond, string> DLenumDateCond = EnumToDictionary<enumDateCond>();
foreach(enumDateCond key in DLenumDateCond.Keys)
{
Console.WriteLine(key.ToString() + " " + DLenumDateCond[key]);
}
}
public static Dictionary<T, string> EnumToDictionary<T>()
where T : struct
{
Type enumType = typeof(T);
// Can't use generic type constraints on value types,
// so have to do check like this
if (enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
Dictionary<T, string> enumDL = new Dictionary<T, string>();
foreach (T enm in Enum.GetValues(enumType))
{
string name = Enum.GetName(enumType, enm);
if (name != null)
{
FieldInfo field = enumType.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
name = attr.Description;
}
}
enumDL.Add(enm, name);
}
return enumDL;
}
}
}

Convert an enum to List<string>

How do I convert the following Enum to a List of strings?
[Flags]
public enum DataSourceTypes
{
None = 0,
Grid = 1,
ExcelFile = 2,
ODBC = 4
};
I couldn't find this exact question, this Enum to List is the closest but I specifically want List<string>?
Use Enum's static method, GetNames. It returns a string[], like so:
Enum.GetNames(typeof(DataSourceTypes))
If you want to create a method that does only this for only one type of enum, and also converts that array to a List, you can write something like this:
public List<string> GetDataSourceTypes()
{
return Enum.GetNames(typeof(DataSourceTypes)).ToList();
}
You will need Using System.Linq; at the top of your class to use .ToList()
I want to add another solution:
In my case, I need to use a Enum group in a drop down button list items. So they might have space, i.e. more user friendly descriptions needed:
public enum CancelReasonsEnum
{
[Description("In rush")]
InRush,
[Description("Need more coffee")]
NeedMoreCoffee,
[Description("Call me back in 5 minutes!")]
In5Minutes
}
In a helper class (HelperMethods) I created the following method:
public static List<string> GetListOfDescription<T>() where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null : Enum.GetValues(t).Cast<Enum>().Select(x => x.GetDescription()).ToList();
}
When you call this helper you will get the list of item descriptions.
List<string> items = HelperMethods.GetListOfDescription<CancelReasonEnum>();
ADDITION:
In any case, if you want to implement this method you need :GetDescription extension for enum. This is what I use.
public static string GetDescription(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =Attribute.GetCustomAttribute(field,typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
return attr.Description;
}
}
}
return null;
/* how to use
MyEnum x = MyEnum.NeedMoreCoffee;
string description = x.GetDescription();
*/
}
In my case, I need to convert it as SelectItem for Checkbox and Radio Button
public class Enum<T> where T : struct, IConvertible
{
public static List<SelectItem> ToSelectItems
{
get
{
if (!typeof(T).IsEnum)
throw new ArgumentException("T must be an enumerated type");
var values = Enum.GetNames(typeof(T));
return values.Select((t, i) => new SelectItem() {Id = i, Name = t}).ToList();
}
}
}

Get a List<string> of my enum attributes with a generic method

At start, we have this basic enum.
public enum E_Levels {
[ValueOfEnum("Low level")]
LOW,
[ValueOfEnum("Normal level")]
NORMAL,
[ValueOfEnum("High level")]
HIGH
}
And I would like to get a List<string> whatever the enum. Something like Extensions.GetValuesOfEnum<E_Levels>() which could return a List<string> with "Low level", "Normal level" and "High level" in it.
StackOF helped me to get one value attribute :
public static class Extensions {
public static string ToValueOfEnum(this Enum value) {
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
return attribs.Length > 0 ? attribs[0].value : null;
}
}
And I can call this method whatever the enum : E_Levels.LOW.ToValueOfEnum().
Furthermore, StackOF helped me to get a List<string> for a specific enum.
I made this method in a controller :
private List<string> GetLevels() {
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>();
foreach(E_Levels l in levels)
levelsToReturn.Add(l.ToValueOfEnum());
return levelsToReturn;
}
But this way requires me to rewrite the same method for each enum.
So I tried to add this generic method my class Extensions :
public static class Extensions {
public static string ToValueOfEnum(this Enum value) {...}
public static List<string> GetValuesOf<T>() {
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(T)).Cast<T>();
foreach(T l in levels)
levelsToReturn.Add(l.ToValueOfEnum());
return levelsToReturn;
}
}
But in my foreach, .ToValueOfEnum() is an unknown method.
So I am in trouble, I hoped I could find a way to not rewrite again and again the same method for each enum...
Let's try to keep this more general purpose.
I have an extension method that could grab attributes off of enum values. This would give you quick access to the attributes.
public static class EnumExtensions
{
public static TAttribute GetAttribute<TAttribute>(this Enum value)
where TAttribute : Attribute
{
var type = value.GetType();
var name = Enum.GetName(type, value);
return type.GetField(name)
.GetCustomAttributes(false)
.OfType<TAttribute>()
.SingleOrDefault();
}
}
Using this, you could create some queries to get what you want.
var valuesOfLevels =
Enum.GetValues(typeof(E_Levels)).Cast<E_Levels>()
.Select(level => level.GetAttribute<ValueOfEnumAttribute>().Value);
So your GetValuesOf() method (which is not a great name for such a specialty method IMHO) can be written like this:
public static List<string> GetValuesOf<TEnum>()
where TEnum : struct // can't constrain to enums so closest thing
{
return Enum.GetValues(typeof(TEnum)).Cast<Enum>()
.Select(val => val.GetAttribute<ValueOfEnumAttribute>().Value)
.ToList();
}
Now you may call the method like so:
var levelValues = GetValueOf<E_Levels>();
// levelValues = { "Low level", "Normal level", "High level" }
You might try casting (Enum)(object)l, changing ToValueOfEnum to take an object, or just inline the method:
public static List<string> GetValuesOf<T>()
{
List<string> levelsToReturn = new List<string>();
var levels = Enum.GetValues(typeof(T)).Cast<T>();
foreach (T value in levels)
{
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
ValueOfEnum[] attribs = fieldInfo.GetCustomAttributes(typeof(ValueOfEnum), false) as ValueOfEnum[];
levelsToReturn.Add(attribs.Length > 0 ? attribs[0].value : null);
}
return levelsToReturn;
}
Here's a one-line solution using the casting approach:
return new List<string>(Enum.GetValues(typeof(T)).Cast<Enum>().Select(x => x.ToValueOfEnum()));
In case you weren't clear on why T wasn't recognized as an Enum like E_Levels is, that's because you didn't specify that T : enum. Unfortunately, you can't specify that in C# (even though the CLR supports it), so other approaches like runtime checking/assuming (such as what I'm suggesting here) or post-compile code modifications (e.g. unconstrained-melody) have to be done.
.Net already has the same attribute Description so you can use this one instead ValueOfEnum.
What about dynamic and extension on type and following example
[TestFixture]
public sealed class ForTest
{
[Test]
public void Test()
{
var values = typeof(Levels).ToValues();
values.ForEach(Console.WriteLine);
}
}
public static class TypeExtensions
{
public static List<string> ToValues(this Type value)
{
var result = new List<string>();
var values = ToConcreteValues(value);
foreach (dynamic item in values)
{
Description attribute = GetAttribute<Description>(item);
result.Add(attribute.Description);
}
return result;
}
private static dynamic ToConcreteValues(Type enumType)
{
Array values = Enum.GetValues(enumType);
Type list = typeof (List<>);
Type resultType = list.MakeGenericType(enumType);
dynamic result = Activator.CreateInstance(resultType);
foreach (object value in values)
{
dynamic concreteValue = Enum.Parse(enumType, value.ToString());
result.Add(concreteValue);
}
return result;
}
private static TAttribute GetAttribute<TAttribute>(dynamic value)
where TAttribute : Attribute
{
Type type = value.GetType();
FieldInfo fieldInfo = type.GetField(Enum.GetName(type, value));
return (TAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof (TAttribute));
}
}
public enum Levels
{
[Description("Low level")]LOW,
[Description("Normal level")] NORMAL,
[Description("High level")] HIGH
}
result output:
Low level
Normal level
High level

Categories

Resources