I would like to know if it is possible to get attributes of the enum
values and not of the enum
itself? For example, suppose I have the following <
Alternatively, you could do the following:
List<SelectListItem> selectListItems = new List<SelectListItem>();
foreach (var item in typeof(PaymentTerm).GetEnumValues())
{
var type = item.GetType();
var name = type.GetField(item.ToString()).GetCustomAttributesData().FirstOrDefault()?.NamedArguments.FirstOrDefault().TypedValue.Value.ToString();
selectListItems.Add(new SelectListItem(name, type.Name));
}
Here is code to get information from a Display attribute. It uses a generic method to retrieve the attribute. If the attribute is not found it converts the enum value to a string with pascal/camel case converted to title case (code obtained here)
public static class EnumHelper
{
// Get the Name value of the Display attribute if the
// enum has one, otherwise use the value converted to title case.
public static string GetDisplayName<TEnum>(this TEnum value)
where TEnum : struct, IConvertible
{
var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>();
return attr == null ? value.ToString().ToSpacedTitleCase() : attr.Name;
}
// Get the ShortName value of the Display attribute if the
// enum has one, otherwise use the value converted to title case.
public static string GetDisplayShortName<TEnum>(this TEnum value)
where TEnum : struct, IConvertible
{
var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>();
return attr == null ? value.ToString().ToSpacedTitleCase() : attr.ShortName;
}
/// <summary>
/// Gets an attribute on an enum field value
/// </summary>
/// <typeparam name="TEnum">The enum type</typeparam>
/// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
/// <param name="value">The enum value</param>
/// <returns>The attribute of type T that exists on the enum value</returns>
private static T GetAttributeOfType<TEnum, T>(this TEnum value)
where TEnum : struct, IConvertible
where T : Attribute
{
return value.GetType()
.GetMember(value.ToString())
.First()
.GetCustomAttributes(false)
.OfType<T>()
.LastOrDefault();
}
}
And this is the extension method for strings for converting to title case:
/// <summary>
/// Converts camel case or pascal case to separate words with title case
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string ToSpacedTitleCase(this string s)
{
//https://stackoverflow.com/a/155486/150342
CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
TextInfo textInfo = cultureInfo.TextInfo;
return textInfo
.ToTitleCase(Regex.Replace(s,
"([a-z](?=[A-Z0-9])|[A-Z](?=[A-Z][a-z]))", "$1 "));
}
Here's the .NET Core version of AdamCrawford's answer, using System.Reflection.TypeExtensions;
public static class EnumHelper
{
/// <summary>
/// Gets an attribute on an enum field value
/// </summary>
/// <typeparam name="T">The type of the attribute you want to retrieve</typeparam>
/// <param name="enumVal">The enum value</param>
/// <returns>The attribute of type T that exists on the enum value</returns>
/// <example>string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;</example>
public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
{
var type = enumVal.GetType();
var memInfo = type.GetMember(enumVal.ToString());
IEnumerable<Attribute> attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (T)attributes?.ToArray()[0];
}
}
Alternatively, you could do the following:
Dictionary<FunkyAttributesEnum, string> description = new Dictionary<FunkyAttributesEnum, string>()
{
{ FunkyAttributesEnum.NameWithoutSpaces1, "Name With Spaces1" },
{ FunkyAttributesEnum.NameWithoutSpaces2, "Name With Spaces2" },
};
And get the description with the following:
string s = description[FunkyAttributesEnum.NameWithoutSpaces1];
In my opinion this is a more efficient way of doing what you want to accomplish, as no reflection is needed..
Fluent one liner...
Here I'm using the DisplayAttribute
which contains both the Name
and Description
properties.
public static DisplayAttribute GetDisplayAttributesFrom(this Enum enumValue, Type enumType)
{
return enumType.GetMember(enumValue.ToString())
.First()
.GetCustomAttribute<DisplayAttribute>();
}
Example
public enum ModesOfTransport
{
[Display(Name = "Driving", Description = "Driving a car")] Land,
[Display(Name = "Flying", Description = "Flying on a plane")] Air,
[Display(Name = "Sea cruise", Description = "Cruising on a dinghy")] Sea
}
void Main()
{
ModesOfTransport TransportMode = ModesOfTransport.Sea;
DisplayAttribute metadata = TransportMode.GetDisplayAttributesFrom(typeof(ModesOfTransport));
Console.WriteLine("Name: {0} \nDescription: {1}", metadata.Name, metadata.Description);
}
Name: Sea cruise
Description: Cruising on a dinghy
Guys if it helps I will share with you my solution: Definition of Custom attribute:
[AttributeUsage(AttributeTargets.Field,AllowMultiple = false)]
public class EnumDisplayName : Attribute
{
public string Name { get; private set; }
public EnumDisplayName(string name)
{
Name = name;
}
}
Now because I needed it inside of HtmlHelper definition of HtmlHelper Extension:
public static class EnumHelper
{
public static string EnumDisplayName(this HtmlHelper helper,EPriceType priceType)
{
//Get every fields from enum
var fields = priceType.GetType().GetFields();
//Foreach field skipping 1`st fieldw which keeps currently sellected value
for (int i = 0; i < fields.Length;i++ )
{
//find field with same int value
if ((int)fields[i].GetValue(priceType) == (int)priceType)
{
//get attributes of found field
var attributes = fields[i].GetCustomAttributes(false);
if (attributes.Length > 0)
{
//return name of found attribute
var retAttr = (EnumDisplayName)attributes[0];
return retAttr.Name;
}
}
}
//throw Error if not found
throw new Exception("Błąd podczas ustalania atrybutów dla typu ceny allegro");
}
}
Hope it helps