Generic Enum to SelectList extension method - c#

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();
}
}

Related

Enum to list as an extension?

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.

ListFor enum flags MVC.Net

My model contains an enum with a flags attribute
[Flags()]
public enum InvestmentAmount
{
[Description("£500 - £5,000")]
ZeroToFiveThousand,
[Description("£5,000 - £10,000")]
FiveThousandToTenThousand,
//Deleted remaining entries for size
}
I want to be able to display this in my view as a multiselectable List box.
Obviously the current helper for Listfor() doesn't support enums.
I've tried rolling my own but just receive
The parameter 'expression' must evaluate to an IEnumerable when
multiple selection is allowed.
when it executes.
public static MvcHtmlString EnumListFor<TModel, TEnum>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TEnum>> expression, object htmlAttributes)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
Type enumType = GetNonNullableModelType(metadata);
IEnumerable<TEnum> values = Enum.GetValues(enumType).Cast<TEnum>();
IEnumerable<SelectListItem> items = from value in values
select new SelectListItem
{
Text = GetEnumDescription(value),
Value = value.ToString(),
Selected = value.Equals(metadata.Model)
};
// If the enum is nullable, add an 'empty' item to the collection
if (metadata.IsNullableValueType)
items = SingleEmptyItem.Concat(items);
RouteValueDictionary htmlattr = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
//htmlattr.Add("multiple", "multiple");
if (expression.GetDescription() != null)
{
htmlattr.Add("data-content", expression.GetDescription());
htmlattr.Add("data-original-title", expression.GetTitle());
htmlattr["class"] = "guidance " + htmlattr["class"];
}
var fieldName = htmlHelper.NameFor(expression).ToString();
return htmlHelper.ListBox(fieldName, items, htmlattr); //Exception thrown here
}
Check out my blog post on a helper method I created to do just this:
http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23
This enables you to do something like this:
//If you don't have an enum value use the type
var enumList = EnumHelper.SelectListFor<MyEnum>();
//If you do have an enum value use the value (the value will be marked as selected)
var enumList = EnumHelper.SelectListFor(myEnumValue);
...which you can then use to build your multi list.
The helper class is as follows:
public static class EnumHelper
{
//Creates a SelectList for a nullable enum value
public static SelectList SelectListFor<T>(T? selected)
where T : struct
{
return selected == null ? SelectListFor<T>()
: SelectListFor(selected.Value);
}
//Creates a SelectList for an enum type
public static SelectList SelectListFor<T>() where T : struct
{
Type t = typeof (T);
if (t.IsEnum)
{
var values = Enum.GetValues(typeof(T)).Cast<enum>()
.Select(e => new { Id = Convert.ToInt32(e), Name = e.GetDescription() });
return new SelectList(values, "Id", "Name");
}
return null;
}
//Creates a SelectList for an enum value
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = Enum.GetValues(t).Cast<Enum>()
.Select(e => new { Id = Convert.ToInt32(e), Name = e.GetDescription() });
return new SelectList(values, "Id", "Name", Convert.ToInt32(selected));
}
return null;
}
// Get the value of the description attribute if the
// enum has one, otherwise use the value.
public static string GetDescription<TEnum>(this TEnum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
if (fi != null)
{
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
}
return value.ToString();
}
}
The error appears to have been occurring because I was binding to just an "InvestmentAmount" where as it appears that ListFor checks if the model is a list.
I've had to change my model to a List and build in binding logic (Through AutoMapper) to convert from the flagged Enum to a List and back.
A nicer solution would be to create a generic HTML ListFor helper to do it. Which is the way I'll tackle it if I need it any more than once.

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

Extension method on enumeration, not instance of enumeration

I have an enumeration for my Things like so:
public enum Things
{
OneThing,
AnotherThing
}
I would like to write an extension method for this enumeration (similar to Prise's answer here) but while that method works on an instance of the enumeration, ala
Things thing; var list = thing.ToSelectList();
I would like it to work on the actual enumeration instead:
var list = Things.ToSelectList();
I could just do
var list = default(Things).ToSelectList();
But I don't like the look of that :)
I have gotten closer with the following extension method:
public static SelectList ToSelectList(this Type type)
{
if (type.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
else
{
return null;
}
}
Used like so:
var list = typeof(Things).ToSelectList();
Can we do any better than that?
Extension methods only work on instances, so it can't be done, but with some well-chosen class/method names and generics, you can produce a result that looks just as good:
public class SelectList
{
// Normal SelectList properties/methods go here
public static SelectList Of<T>()
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
return null;
}
}
Then you can get your select list like this:
var list = SelectList.Of<Things>();
IMO this reads a lot better than Things.ToSelectList().
No.
The best you can do is put it on a static class, like this:
public static class ThingsUtils {
public static SelectList ToSelectList() { ... }
}
Aaronaught's answer is really great, based on that I made the following implementation:
public class SelectList
{
public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible
{
Type t = typeof(T);
if (t.IsEnum)
{
return Enum.GetValues(t).Cast<Enum>();
}
throw new ArgumentException("<T> must be an enumerated type.");
}
}
In my opinion it's a little bit safer, as you can - almost - call it only with Enums, and of course instead of the throw you can simply return null if you want an exception-free version.
I use 'Type' instead of 'Enum' to add extension. Then I can get any type of list back from the method. Here it returns string values:
public static string[] AllDescription(this Type enumType)
{
if (!enumType.IsEnum) return null;
var list = new List<string>();
var values = Enum.GetValues(enumType);
foreach (var item in values)
{
// add any combination of information to list here:
list.Add(string.Format("{0}", item));
//this one gets the values from the [Description] Attribute that I usually use to fill drop downs
//list.Add(((Enum) item).GetDescription());
}
return list.ToArray();
}
Later I could use this syntax to get what I want:
var listOfThings = typeof (Things).AllDescription();
#Aaronaught has a very good answer. To extend his answer, you can also even make it more generic. I have this in a global library...
public static IQueryable GetAllEnumValues<T>()
{
IQueryable retVal = null;
Type targetType = typeof(T);
if(targetType.IsEnum)
{
retVal = Enum.GetValues(targetType).AsQueryable();
}
return retVal;
}
Now you have de-coupled this functionality from the SelectList class. So you can call this in your SelectList methods, or anywhere else for that matter.
public class SelectList
{
public static SelectList Of<T>
{
IQueryable enumValues = GetAllEnumValues<T>();
var values =
from Enum e in enumValues
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
}
In my opinion, this is the cleanest way. Why?
It works for any System.Enum
The extension method itself is cleaner.
To call it you just add new and that's a small trade off (because it has to have an instance in order to work.
You aren't passing null around and it literally won't compile if you try to use it with another type.
Usage:
(new Things()).ToSelectList()
Extension Method:
[Extension()]
public SelectList ToSelectList(System.Enum source)
{
var values = from Enum e in Enum.GetValues(source.GetType)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
The closest you can come, I think, is to dummy things up a bit to work like an enum without being one. Here's what I've come up with--it seems like a lot of work just to plop a static method on an enumeration, although I do understand the programming appeal of it:
public class PseudoEnum
{
public const int FirstValue = 1;
private static PseudoEnum FirstValueObject = new PseudoEnum(1);
public const int SecondValue = 2;
private static PseudoEnum SecondValueObject = new PseudoEnum(2);
private int intValue;
// This prevents instantation; note that we cannot mark the class static
private PseudoEnum() {}
private PseudoEnum(int _intValue)
{
intValue = _intValue;
}
public static implicit operator int(PseudoEnum i)
{
return i.intValue;
}
public static implicit operator PseudoEnum(int i)
{
switch (i)
{
case FirstValue :
return FirstValueObject;
case SecondValue :
return SecondValueObject;
default:
throw new InvalidCastException();
}
}
public static void DoSomething(PseudoEnum pe)
{
switch (pe)
{
case PseudoEnum.FirstValue:
break;
case PseudoEnum.SecondValue:
break;
}
}
}

Categories

Resources