using System.Collections.Generic;
using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
public static void Main()
Console.WriteLine(MyTestEnum.ValueTwo.GetDisplayName());
Console.WriteLine(MyTestEnum.ValueTwo.GetDescription());
Console.WriteLine(MyTestEnum.ValueTwo.GetDisplayMetadata());
Console.WriteLine(EnumHelper.GetEnumValueByDisplayName<MyTestEnum>("My Value Two"));
var vals = EnumHelper.GetDisplayValues<MyTestEnum>();
Console.WriteLine(string.Join(Environment.NewLine, vals));
[Display(Name = "My Value One", Description = "My Sameple Description One", Order = 1)]
[Display(Name = "My Value Two", Description = "My Sameple Description Two", Order = 2)]
public static class EnumHelper
private static ConcurrentDictionary<Type, IReadOnlyDictionary<int, IdName>> _typeMetadata
= new ConcurrentDictionary<Type, IReadOnlyDictionary<int, IdName>>();
public static int ToId<T>(this T enumVal) where T : Enum, IConvertible
return enumVal.ToInt32(CultureInfo.InvariantCulture);
public static T ToEnum<T>(this int id) where T : Enum
if (!Enum.IsDefined(typeof(T), id))
throw new FormatException($"Value {id} is not defined in enum type {typeof(T)}");
return (T)Enum.ToObject(typeof(T), id);
public static string GetDisplayName<T>(this T enumValue) where T : struct, Enum, IConvertible
var typeMetadata = _typeMetadata.GetOrAdd(typeof(T), CreateMetadataGenerator<T>());
var key = enumValue.ToId();
return typeMetadata.TryGetValue(key, out var itemMetadata)
public static string GetDescription<T>(this T enumValue) where T : struct, Enum, IConvertible
var typeMetadata = _typeMetadata.GetOrAdd(typeof(T), CreateMetadataGenerator<T>());
var key = enumValue.ToId();
return typeMetadata.TryGetValue(key, out var itemMetadata)
? itemMetadata.Description ?? enumValue.ToString()
public static IdName GetDisplayMetadata<T>(this T enumValue) where T : struct, Enum, IConvertible
var typeMetadata = _typeMetadata.GetOrAdd(typeof(T), CreateMetadataGenerator<T>());
var key = enumValue.ToId();
return typeMetadata.TryGetValue(key, out var itemMetadata)
public static IEnumerable<IdName> GetDisplayValues<T>(bool includeDefaultValue = false) where T : struct, Enum, IConvertible
var typeMetadata = _typeMetadata.GetOrAdd(typeof(T), CreateMetadataGenerator<T>());
.Where(kv => includeDefaultValue || kv.Key != default(int))
.OrderBy(v => v.SortOrder);
public static T GetEnumValueByDisplayName<T>(string displayName) where T : struct, Enum, IConvertible
var typeMetadata = _typeMetadata.GetOrAdd(typeof(T), CreateMetadataGenerator<T>());
.Where(kv => kv.Value.Name == displayName)
.Select(kv => kv.Value.Id.ToEnum<T>())
private static Func<Type, IReadOnlyDictionary<int, IdName>> CreateMetadataGenerator<T>() where T : struct, Enum, IConvertible
var enumValues = Enum.GetValues<T>();
.Where(m => m.MemberType == MemberTypes.Field)
DisplayInfo = m.GetCustomAttribute<DisplayAttribute>(),
.Join(enumValues, s1 => s1.EnumName, s2 => s2.ToString(), (s1, s2) => new IdName
Name = s1.DisplayInfo?.Name ?? s2.ToString(),
Description = s1.DisplayInfo?.Description ?? s2.ToString(),
SortOrder = s1.DisplayInfo?.Order ?? s1.Index
.ToDictionary(d => d.Id, d => d)
public int Id { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
public string Description { get; set; }
public static IdName MakeIdName(int id, string name)
IdName idn = new IdName();
public override bool Equals(object o)
IdName idn = o as IdName;
public bool Equals(IdName idn)
return Id == idn.Id && Name == idn.Name;
public override int GetHashCode()
return Id.GetHashCode() ^ Name.GetHashCode();
public static bool operator ==(IdName left, IdName right)
return Object.Equals(left, right);
public static bool operator !=(IdName left, IdName right)
public override string ToString()
return $"{{Id:{Id}, Name:\"{Name}\", Description:\"{Description}\", SortOrder:{SortOrder}}}";