This question already has answers here:
Associating enums with strings in C#
(38 answers)
Closed 9 years ago.
I have the following enum
public enum myEnum
{
ThisNameWorks,
This Name doesn't work
Neither.does.this;
}
Is it not possible to have enums with "friendly names"?
You could use the Description attribute, as Yuriy suggested. The following extension method makes it easy to get the description for a given value of the enum:
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;
}
You can use it like this:
public enum MyEnum
{
[Description("Description for Foo")]
Foo,
[Description("Description for Bar")]
Bar
}
MyEnum x = MyEnum.Foo;
string description = x.GetDescription();
Enum value names must follow the same naming rules as all identifiers in C#, therefore only first name is correct.
If you have the following enum:
public enum MyEnum {
First,
Second,
Third
}
You can declare Extension Methods for MyEnum (like you can for any other type). I just whipped this up:
namespace Extension {
public static class ExtensionMethods {
public static string EnumValue(this MyEnum e) {
switch (e) {
case MyEnum.First:
return "First Friendly Value";
case MyEnum.Second:
return "Second Friendly Value";
case MyEnum.Third:
return "Third Friendly Value";
}
return "Horrible Failure!!";
}
}
}
With this Extension Method, the following is now legal:
Console.WriteLine(MyEnum.First.EnumValue());
Hope this helps!!
No, but you can use the DescriptionAttribute to accomplish what you're looking for.
You can use the Description attribute to get that friendly name. You can use the code below:
public static string ToStringEnums(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();
}
An example of when you would want to use this method: When your enum value is EncryptionProviderType and you want enumVar.Tostring() to return "Encryption Provider Type".
Prerequisite: All enum members should be applied with the attribute [Description("String to be returned by Tostring()")].
Example enum:
enum ExampleEnum
{
[Description("One is one")]
ValueOne = 1,
[Description("Two is two")]
ValueTow = 2
}
And in your class, you would use it like this:
ExampleEnum enumVar = ExampleEnum.ValueOne;
Console.WriteLine(ToStringEnums(enumVar));
One problem with this trick is that description attribute cannot be localized. I do like a technique by Sacha Barber where he creates his own version of Description attribute which would pick up values from the corresponding resource manager.
http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx
Although the article is around a problem that's generally faced by WPF developers when binding to enums, you can jump directly to the part where he creates the LocalizableDescriptionAttribute.
Some great solutions have already been posted. When I encountered this problem, I wanted to go both ways: convert an enum into a description, and convert a string matching a description into an enum.
I have two variants, slow and fast. Both convert from enum to string and string to enum. My problem is that I have enums like this, where some elements need attributes and some don't. I don't want to put attributes on elements that don't need them. I have about a hundred of these total currently:
public enum POS
{
CC, // Coordinating conjunction
CD, // Cardinal Number
DT, // Determiner
EX, // Existential there
FW, // Foreign Word
IN, // Preposision or subordinating conjunction
JJ, // Adjective
[System.ComponentModel.Description("WP$")]
WPDollar, //$ Possessive wh-pronoun
WRB, // Wh-adverb
[System.ComponentModel.Description("#")]
Hash,
[System.ComponentModel.Description("$")]
Dollar,
[System.ComponentModel.Description("''")]
DoubleTick,
[System.ComponentModel.Description("(")]
LeftParenth,
[System.ComponentModel.Description(")")]
RightParenth,
[System.ComponentModel.Description(",")]
Comma,
[System.ComponentModel.Description(".")]
Period,
[System.ComponentModel.Description(":")]
Colon,
[System.ComponentModel.Description("``")]
DoubleBackTick,
};
The first method for dealing with this is slow, and is based on suggestions I saw here and around the net. It's slow because we are reflecting for every conversion:
using System;
using System.Collections.Generic;
namespace CustomExtensions
{
/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
public static class EnumExtensions
{
/// <summary>
/// Gets the description string, if available. Otherwise returns the name of the enum field
/// LthWrapper.POS.Dollar.GetString() yields "$", an impossible control character for enums
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string GetStringSlow(this Enum value)
{
Type type = value.GetType();
string name = Enum.GetName(type, value);
if (name != null)
{
System.Reflection.FieldInfo field = type.GetField(name);
if (field != null)
{
System.ComponentModel.DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
if (attr != null)
{
//return the description if we have it
name = attr.Description;
}
}
}
return name;
}
/// <summary>
/// Converts a string to an enum field using the string first; if that fails, tries to find a description
/// attribute that matches.
/// "$".ToEnum<LthWrapper.POS>() yields POS.Dollar
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static T ToEnumSlow<T>(this string value) //, T defaultValue)
{
T theEnum = default(T);
Type enumType = typeof(T);
//check and see if the value is a non attribute value
try
{
theEnum = (T)Enum.Parse(enumType, value);
}
catch (System.ArgumentException e)
{
bool found = false;
foreach (T enumValue in Enum.GetValues(enumType))
{
System.Reflection.FieldInfo field = enumType.GetField(enumValue.ToString());
System.ComponentModel.DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
if (attr != null && attr.Description.Equals(value))
{
theEnum = enumValue;
found = true;
break;
}
}
if( !found )
throw new ArgumentException("Cannot convert " + value + " to " + enumType.ToString());
}
return theEnum;
}
}
}
The problem with this is that you're doing reflection every time. I haven't measured the performance hit from doing so, but it seems alarming. Worse we are computing these expensive conversions repeatedly, without caching them.
Instead we can use a static constructor to populate some dictionaries with this conversion information, then just look up this information when needed. Apparently static classes (required for extension methods) can have constructors and fields :)
using System;
using System.Collections.Generic;
namespace CustomExtensions
{
/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
/// I'm not sure this is a good idea. While it makes that section of the code much much nicer to maintain, it
/// also incurs a performance hit via reflection. To circumvent this, I've added a dictionary so all the lookup can be done once at
/// load time. It requires that all enums involved in this extension are in this assembly.
/// </summary>
public static class EnumExtensions
{
//To avoid collisions, every Enum type has its own hash table
private static readonly Dictionary<Type, Dictionary<object,string>> enumToStringDictionary = new Dictionary<Type,Dictionary<object,string>>();
private static readonly Dictionary<Type, Dictionary<string, object>> stringToEnumDictionary = new Dictionary<Type, Dictionary<string, object>>();
static EnumExtensions()
{
//let's collect the enums we care about
List<Type> enumTypeList = new List<Type>();
//probe this assembly for all enums
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
Type[] exportedTypes = assembly.GetExportedTypes();
foreach (Type type in exportedTypes)
{
if (type.IsEnum)
enumTypeList.Add(type);
}
//for each enum in our list, populate the appropriate dictionaries
foreach (Type type in enumTypeList)
{
//add dictionaries for this type
EnumExtensions.enumToStringDictionary.Add(type, new Dictionary<object,string>() );
EnumExtensions.stringToEnumDictionary.Add(type, new Dictionary<string,object>() );
Array values = Enum.GetValues(type);
//its ok to manipulate 'value' as object, since when we convert we're given the type to cast to
foreach (object value in values)
{
System.Reflection.FieldInfo fieldInfo = type.GetField(value.ToString());
//check for an attribute
System.ComponentModel.DescriptionAttribute attribute =
Attribute.GetCustomAttribute(fieldInfo,
typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
//populate our dictionaries
if (attribute != null)
{
EnumExtensions.enumToStringDictionary[type].Add(value, attribute.Description);
EnumExtensions.stringToEnumDictionary[type].Add(attribute.Description, value);
}
else
{
EnumExtensions.enumToStringDictionary[type].Add(value, value.ToString());
EnumExtensions.stringToEnumDictionary[type].Add(value.ToString(), value);
}
}
}
}
public static string GetString(this Enum value)
{
Type type = value.GetType();
string aString = EnumExtensions.enumToStringDictionary[type][value];
return aString;
}
public static T ToEnum<T>(this string value)
{
Type type = typeof(T);
T theEnum = (T)EnumExtensions.stringToEnumDictionary[type][value];
return theEnum;
}
}
}
Look how tight the conversion methods are now. The only flaw I can think of is that this requires all the converted enums to be in the current assembly. Also, I only bother with exported enums, but you could change that if you wish.
This is how to call the methods
string x = LthWrapper.POS.Dollar.GetString();
LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();
public enum myEnum
{
ThisNameWorks,
This_Name_can_be_used_instead,
}
After reading many resources regarding this topic, including StackOverFlow, I find that not all solutions are working properly. Below is our attempt to fix this.
Basically, We take the friendly name of an Enum from a DescriptionAttribute if it exists.
If it does not We use RegEx to determine the words within the Enum name and add spaces.
Next version, we will use another Attribute to flag whether we can/should take the friendly name from a localizable resource file.
Below are the test cases. Please report if you have another test case that do not pass.
public static class EnumHelper
{
public static string ToDescription(Enum value)
{
if (value == null)
{
return string.Empty;
}
if (!Enum.IsDefined(value.GetType(), value))
{
return string.Empty;
}
FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
if (fieldInfo != null)
{
DescriptionAttribute[] attributes =
fieldInfo.GetCustomAttributes(typeof (DescriptionAttribute), false) as DescriptionAttribute[];
if (attributes != null && attributes.Length > 0)
{
return attributes[0].Description;
}
}
return StringHelper.ToFriendlyName(value.ToString());
}
}
public static class StringHelper
{
public static bool IsNullOrWhiteSpace(string value)
{
return value == null || string.IsNullOrEmpty(value.Trim());
}
public static string ToFriendlyName(string value)
{
if (value == null) return string.Empty;
if (value.Trim().Length == 0) return string.Empty;
string result = value;
result = string.Concat(result.Substring(0, 1).ToUpperInvariant(), result.Substring(1, result.Length - 1));
const string pattern = #"([A-Z]+(?![a-z])|\d+|[A-Z][a-z]+|(?![A-Z])[a-z]+)+";
List<string> words = new List<string>();
Match match = Regex.Match(result, pattern);
if (match.Success)
{
Group group = match.Groups[1];
foreach (Capture capture in group.Captures)
{
words.Add(capture.Value);
}
}
return string.Join(" ", words.ToArray());
}
}
[TestMethod]
public void TestFriendlyName()
{
string[][] cases =
{
new string[] {null, string.Empty},
new string[] {string.Empty, string.Empty},
new string[] {" ", string.Empty},
new string[] {"A", "A"},
new string[] {"z", "Z"},
new string[] {"Pascal", "Pascal"},
new string[] {"camel", "Camel"},
new string[] {"PascalCase", "Pascal Case"},
new string[] {"ABCPascal", "ABC Pascal"},
new string[] {"PascalABC", "Pascal ABC"},
new string[] {"Pascal123", "Pascal 123"},
new string[] {"Pascal123ABC", "Pascal 123 ABC"},
new string[] {"PascalABC123", "Pascal ABC 123"},
new string[] {"123Pascal", "123 Pascal"},
new string[] {"123ABCPascal", "123 ABC Pascal"},
new string[] {"ABC123Pascal", "ABC 123 Pascal"},
new string[] {"camelCase", "Camel Case"},
new string[] {"camelABC", "Camel ABC"},
new string[] {"camel123", "Camel 123"},
};
foreach (string[] givens in cases)
{
string input = givens[0];
string expected = givens[1];
string output = StringHelper.ToFriendlyName(input);
Assert.AreEqual(expected, output);
}
}
}
They follow the same naming rules as variable names.
Therefore they should not contain spaces.
Also what you are suggesting would be very bad practice anyway.
Enum names live under the same rules as normal variable names, i.e. no spaces or dots in the middle of the names... I still consider the first one to be rather friendly though...
This is a terrible idea, but it does work.
public enum myEnum
{
ThisNameWorks,
ThisNameDoesntWork149141331,// This Name doesn't work
NeitherDoesThis1849204824// Neither.does.this;
}
class Program
{
private static unsafe void ChangeString(string original, string replacement)
{
if (original.Length < replacement.Length)
throw new ArgumentException();
fixed (char* pDst = original)
fixed (char* pSrc = replacement)
{
// Update the length of the original string
int* lenPtr = (int*)pDst;
lenPtr[-1] = replacement.Length;
// Copy the characters
for (int i = 0; i < replacement.Length; i++)
pDst[i] = pSrc[i];
}
}
public static unsafe void Initialize()
{
ChangeString(myEnum.ThisNameDoesntWork149141331.ToString(), "This Name doesn't work");
ChangeString(myEnum.NeitherDoesThis1849204824.ToString(), "Neither.does.this");
}
static void Main(string[] args)
{
Console.WriteLine(myEnum.ThisNameWorks);
Console.WriteLine(myEnum.ThisNameDoesntWork149141331);
Console.WriteLine(myEnum.NeitherDoesThis1849204824);
Initialize();
Console.WriteLine(myEnum.ThisNameWorks);
Console.WriteLine(myEnum.ThisNameDoesntWork149141331);
Console.WriteLine(myEnum.NeitherDoesThis1849204824);
}
Requirements
Your enum names must have the same number of characters or more than the string that you want to it to be.
Your enum names shouldn't be repeated anywhere, just in case string interning messes things up
Why this is a bad idea (a few reasons)
Your enum names become ugly beause of the requirements
It relies on you calling the initialization method early enough
Unsafe pointers
If the internal format of string changes, e.g. if the length field is moved, you're screwed
If Enum.ToString() is ever changed so that it returns only a copy, you're screwed
Raymond Chen will complain about your use of undocumented features, and how it's your fault that the CLR team couldn't make an optimization to cut run time by 50%, during his next .NET week.
I suppose that you want to show your enum values to the user, therefore, you want them to have some friendly name.
Here's my suggestion:
Use an enum type pattern. Although it takes some effort to implement, it is really worth it.
public class MyEnum
{
public static readonly MyEnum Enum1=new MyEnum("This will work",1);
public static readonly MyEnum Enum2=new MyEnum("This.will.work.either",2);
public static readonly MyEnum[] All=new []{Enum1,Enum2};
private MyEnum(string name,int value)
{
Name=name;
Value=value;
}
public string Name{get;set;}
public int Value{get;set;}
public override string ToString()
{
return Name;
}
}
Related
I have the following enum:
public enum Urgency {
VeryHigh = 1,
High = 2,
Routine = 4
}
I can fetch an enum "value" as string like this:
((int)Urgency.Routine).ToString() // returns "4"
Note: This is different from:
Urgency.Routine.ToString() // returns "Routine"
(int)Urgency.Routine // returns 4
Is there a way I can create an extension class, or a static utliity class, that would provide some syntactical sugar? :)
You should just be able to use the overloads of Enums ToString method to give it a format string, this will print out the value of the enum as a string.
public static class Program
{
static void Main(string[] args)
{
var val = Urgency.High;
Console.WriteLine(val.ToString("D"));
}
}
public enum Urgency
{
VeryHigh = 1,
High = 2,
Low = 4
}
In order to achieve more "human readable" descriptions for enums (e.g. "Very High" rather than "VeryHigh" in your example) I have decorated enum values with attribute as follows:
public enum MeasurementType
{
Each,
[DisplayText("Lineal Metres")]
LinealMetre,
[DisplayText("Square Metres")]
SquareMetre,
[DisplayText("Cubic Metres")]
CubicMetre,
[DisplayText("Per 1000")]
Per1000,
Other
}
public class DisplayText : Attribute
{
public DisplayText(string Text)
{
this.text = Text;
}
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
}
Then, used an extension method like this:
public static string ToDescription(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(DisplayText),
false);
if (attrs != null && attrs.Length > 0)
return ((DisplayText)attrs[0]).Text;
}
return en.ToString();
}
You can then just call myEnum.ToDescription() in order to display your enum as more readable text.
If you want to just deal with this enum, use Mark Byer's solution.
For a more general solution:
public static string NumberString(this Enum enVal)
{
return Convert.ToDecimal(enVal).ToString("0");
}
Converting to decimal means you don't need to deal with the 8 different allowed underlying integral types explicitly, as all of them convert losslessly to decimal but not to each other (ulong and long don't convert losslessly between each other but both can handle all the rest). Doing that would probably be faster (esp. if you pick well in your order of comparison), but a lot more verbose for relatively little gain.
Edit:
The above isn't as good as Frankentosh's though, Frankentosh saw through the question to the real problem and solves it very eloquently.
Great stuff ... I have now added an extension method to my project
public static class EnumExtensions
{
public static string NumberString(this Enum enVal)
{
return enVal.ToString("D");
}
}
Now I can get the int value - as a string - by calling Urgency.Routine.NumberString(); Thanks to Frankentosh and Jon :)
a simple approach
((Urgency)4).ToString() // returns "Routine"
You can write an extension method for your specific type:
public static class UrgencyExtension
{
public static string ToIntegerString(this Urgency u)
{
return ((int)u).ToString();
}
}
Use as follows:
Urgency u = Urgency.Routine;
string s = u.ToIntegerString();
How about a little reflection? Should work with all underlying types.
public static class EnumTools
{
public static string ToRawValueString(this Enum e)
{
return e
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Static)
.First(f => f.Name==e.ToString())
.GetRawConstantValue()
.ToString();
}
}
Then:
Console.WriteLine(Urgency.High.ToRawValueString()); //Writes "2"
If you wanted, you could make the extension method work for all enums:
public static string ToValueString(this Enum enumValue)
{
if (enumValue.GetType().GetEnumUnderlyingType() == typeof(int))
return ((int)(object)enumValue).ToString();
else if (enumValue.GetType().GetEnumUnderlyingType() == typeof(byte))
return ((byte)(object)enumValue).ToString();
...
}
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 enum:
using System.ComponentModel; // for DescriptionAttribute
enum FunkyAttributesEnum
{
[Description("Name With Spaces1")]
NameWithoutSpaces1,
[Description("Name With Spaces2")]
NameWithoutSpaces2
}
What I want is given the enum type, produce 2-tuples of enum string value and its description.
Value was easy:
Array values = System.Enum.GetValues(typeof(FunkyAttributesEnum));
foreach (int value in values)
Tuple.Value = Enum.GetName(typeof(FunkyAttributesEnum), value);
But how do I get description attribute's value, to populate Tuple.Desc? I can think of how to do it if the Attribute belongs to the enum itself, but I am at a loss as to how to get it from the value of the enum.
This should do what you need.
try
{
var enumType = typeof(FunkyAttributesEnum);
var memberInfos =
enumType.GetMember(FunkyAttributesEnum.NameWithoutSpaces1.ToString());
var enumValueMemberInfo = memberInfos.FirstOrDefault(m =>
m.DeclaringType == enumType);
var valueAttributes =
enumValueMemberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
var description = ((DescriptionAttribute)valueAttributes[0]).Description;
}
catch
{
return FunkyAttributesEnum.NameWithoutSpaces1.ToString()
}
This piece of code should give you a nice little extension method on any enum that lets you retrieve a generic attribute. I believe it's different to the lambda function above because it's simpler to use and slightly - you only need to pass in the generic type.
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><![CDATA[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());
var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);
return (attributes.Length > 0) ? (T)attributes[0] : null;
}
}
Usage would then be:
string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;
This is a generic implementation using a lambda for the selection
public static Expected GetAttributeValue<T, Expected>(this Enum enumeration, Func<T, Expected> expression)
where T : Attribute
{
T attribute =
enumeration
.GetType()
.GetMember(enumeration.ToString())
.Where(member => member.MemberType == MemberTypes.Field)
.FirstOrDefault()
.GetCustomAttributes(typeof(T), false)
.Cast<T>()
.SingleOrDefault();
if (attribute == null)
return default(Expected);
return expression(attribute);
}
Call it like this:
string description = targetLevel.GetAttributeValue<DescriptionAttribute, string>(x => x.Description);
I've merged a couple of the answers here to create a little more extensible solution. I'm providing it just in case it's helpful to anyone else in the future. Original posting here.
using System;
using System.ComponentModel;
public static class EnumExtensions {
// This extension method is broken out so you can use a similar pattern with
// other MetaData elements in the future. This is your base method for each.
public static T GetAttribute<T>(this Enum value) where T : Attribute {
var type = value.GetType();
var memberInfo = type.GetMember(value.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
return attributes.Length > 0
? (T)attributes[0]
: null;
}
// This method creates a specific call to the above method, requesting the
// Description MetaData attribute.
public static string ToName(this Enum value) {
var attribute = value.GetAttribute<DescriptionAttribute>();
return attribute == null ? value.ToString() : attribute.Description;
}
}
This solution creates a pair of extension methods on Enum. The first allows you to use reflection to retrieve any attribute associated with your value. The second specifically calls retrieves the DescriptionAttribute and returns it's Description value.
As an example, consider using the DescriptionAttribute attribute from System.ComponentModel
using System.ComponentModel;
public enum Days {
[Description("Sunday")]
Sun,
[Description("Monday")]
Mon,
[Description("Tuesday")]
Tue,
[Description("Wednesday")]
Wed,
[Description("Thursday")]
Thu,
[Description("Friday")]
Fri,
[Description("Saturday")]
Sat
}
To use the above extension method, you would now simply call the following:
Console.WriteLine(Days.Mon.ToName());
or
var day = Days.Mon;
Console.WriteLine(day.ToName());
In addition to AdamCrawford response, I've further created a more specialized extension methods that feed of it to get the description.
public static string GetAttributeDescription(this Enum enumValue)
{
var attribute = enumValue.GetAttributeOfType<DescriptionAttribute>();
return attribute == null ? String.Empty : attribute.Description;
}
hence, to get the description, you could either use the original extension method as
string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description
or you could simply call the the extension method here as:
string desc = myEnumVariable.GetAttributeDescription();
Which should hopefully make your code a bit more readable.
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);
}
Output
Name: Sea cruise
Description: Cruising on a dinghy
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 "));
}
Get the dictionary from enum.
public static IDictionary<string, int> ToDictionary(this Type enumType)
{
return Enum.GetValues(enumType)
.Cast<object>()
.ToDictionary(v => ((Enum)v).ToEnumDescription(), k => (int)k);
}
Now call this like...
var dic = typeof(ActivityType).ToDictionary();
EnumDecription Ext Method
public static string ToEnumDescription(this Enum en) //ext method
{
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();
}
public enum ActivityType
{
[Description("Drip Plan Email")]
DripPlanEmail = 1,
[Description("Modification")]
Modification = 2,
[Description("View")]
View = 3,
[Description("E-Alert Sent")]
EAlertSent = 4,
[Description("E-Alert View")]
EAlertView = 5
}
I this answer to setup a combo box from an enum attributes which was great.
I then needed to code the reverse so that I can get the selection from the box and return the enum in the correct type.
I also modified the code to handle the case where an attribute was missing
For the benefits of the next person, here is my final solution
public static class Program
{
static void Main(string[] args)
{
// display the description attribute from the enum
foreach (Colour type in (Colour[])Enum.GetValues(typeof(Colour)))
{
Console.WriteLine(EnumExtensions.ToName(type));
}
// Get the array from the description
string xStr = "Yellow";
Colour thisColour = EnumExtensions.FromName<Colour>(xStr);
Console.ReadLine();
}
public enum Colour
{
[Description("Colour Red")]
Red = 0,
[Description("Colour Green")]
Green = 1,
[Description("Colour Blue")]
Blue = 2,
Yellow = 3
}
}
public static class EnumExtensions
{
// This extension method is broken out so you can use a similar pattern with
// other MetaData elements in the future. This is your base method for each.
public static T GetAttribute<T>(this Enum value) where T : Attribute
{
var type = value.GetType();
var memberInfo = type.GetMember(value.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false);
// check if no attributes have been specified.
if (((Array)attributes).Length > 0)
{
return (T)attributes[0];
}
else
{
return null;
}
}
// This method creates a specific call to the above method, requesting the
// Description MetaData attribute.
public static string ToName(this Enum value)
{
var attribute = value.GetAttribute<DescriptionAttribute>();
return attribute == null ? value.ToString() : attribute.Description;
}
/// <summary>
/// Find the enum from the description attribute.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="desc"></param>
/// <returns></returns>
public static T FromName<T>(this string desc) where T : struct
{
string attr;
Boolean found = false;
T result = (T)Enum.GetValues(typeof(T)).GetValue(0);
foreach (object enumVal in Enum.GetValues(typeof(T)))
{
attr = ((Enum)enumVal).ToName();
if (attr == desc)
{
result = (T)enumVal;
found = true;
break;
}
}
if (!found)
{
throw new Exception();
}
return result;
}
}
}
Performance matters
If you want better performance this is the way to go:
public static class AdvancedEnumExtensions
{
/// <summary>
/// Gets the custom attribute <typeparamref name="T"/> for the enum constant, if such a constant is defined and has such an attribute; otherwise null.
/// </summary>
public static T GetCustomAttribute<T>(this Enum value) where T : Attribute
{
return GetField(value)?.GetCustomAttribute<T>(inherit: false);
}
/// <summary>
/// Gets the FieldInfo for the enum constant, if such a constant is defined; otherwise null.
/// </summary>
public static FieldInfo GetField(this Enum value)
{
ulong u64 = ToUInt64(value);
return value
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
.Where(f => ToUInt64(f.GetRawConstantValue()) == u64)
.FirstOrDefault();
}
/// <summary>
/// Checks if an enum constant is defined for this enum value
/// </summary>
public static bool IsDefined(this Enum value)
{
return GetField(value) != null;
}
/// <summary>
/// Converts the enum value to UInt64
/// </summary>
public static ulong ToUInt64(this Enum value) => ToUInt64((object)value);
private static ulong ToUInt64(object value)
{
switch (Convert.GetTypeCode(value))
{
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Char:
case TypeCode.Boolean:
return Convert.ToUInt64(value, CultureInfo.InvariantCulture);
default: throw new InvalidOperationException("UnknownEnumType");
}
}
}
Why does this have better performance?
Because the built-in methods all use code very similar to this except they also run a bunch of other code we don't care about. C#'s Enum code is quite horrible in general.
The above code has been Linq-ified and streamlined so it only contains the bits we care about.
Why is the built-in code slow?
First regarding Enum.ToString() -vs- Enum.GetName(..)
Always use the latter. (Or better yet neither, as will become clear below.)
ToString() uses the latter internally, but again, also does a bunch of other stuff we don't want, e.g. tries to combine flags, print out numbers etc. We are only interested in constants defined inside the enum.
Enum.GetName in turn gets all fields, creates a string array for all names, uses the above ToUInt64 on all of their RawConstantValues to create an UInt64 array of all values, sorts both arrays according to the UInt64 value, and finally gets the name from the name-array by doing a BinarySearch in the UInt64-array to find the index of the value we wanted.
...and then we throw the fields and the sorted arrays away use that name to find the field again.
One word: "Ugh!"
For some programmer humor, a one liner as a joke:
public static string GetDescription(this Enum value) => value.GetType().GetMember(value.ToString()).First().GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute ? attribute.Description : string.Empty;
In a more readable form:
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
public static class EnumExtensions
{
// get description from enum:
public static string GetDescription(this Enum value)
{
return value.GetType().
GetMember(value.ToString()).
First().
GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute
? attribute.Description
: throw new Exception($"Enum member '{value.GetType()}.{value}' doesn't have a [DescriptionAttribute]!");
}
// get enum from description:
public static T GetEnum<T>(this string description) where T : Enum
{
foreach (FieldInfo fieldInfo in typeof(T).GetFields())
{
if (fieldInfo.GetCustomAttribute<DescriptionAttribute>() is DescriptionAttribute attribute && attribute.Description == description)
return (T)fieldInfo.GetRawConstantValue();
}
throw new Exception($"Enum '{typeof(T)}' doesn't have a member with a [DescriptionAttribute('{description}')]!");
}
}
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];
}
}
Adding my solution for Net Framework and NetCore.
I used this for my Net Framework implementation:
public static class EnumerationExtension
{
public static string Description( this Enum value )
{
// get attributes
var field = value.GetType().GetField( value.ToString() );
var attributes = field.GetCustomAttributes( typeof( DescriptionAttribute ), false );
// return description
return attributes.Any() ? ( (DescriptionAttribute)attributes.ElementAt( 0 ) ).Description : "Description Not Found";
}
}
This doesn't work for NetCore so I modified it to do this:
public static class EnumerationExtension
{
public static string Description( this Enum value )
{
// get attributes
var field = value.GetType().GetField( value.ToString() );
var attributes = field.GetCustomAttributes( false );
// Description is in a hidden Attribute class called DisplayAttribute
// Not to be confused with DisplayNameAttribute
dynamic displayAttribute = null;
if (attributes.Any())
{
displayAttribute = attributes.ElementAt( 0 );
}
// return description
return displayAttribute?.Description ?? "Description Not Found";
}
}
Enumeration Example:
public enum ExportTypes
{
[Display( Name = "csv", Description = "text/csv" )]
CSV = 0
}
Sample Usage for either static added:
var myDescription = myEnum.Description();
If your enum contains a value like Equals you might bump into a few bugs using some extensions in a lot of answers here. This is because it is normally assumed that typeof(YourEnum).GetMember(YourEnum.Value) would return only one value, which is the MemberInfo of your enum. Here's a slightly safer version Adam Crawford's answer.
public static class AttributeExtensions
{
#region Methods
public static T GetAttribute<T>(this Enum enumValue) where T : Attribute
{
var type = enumValue.GetType();
var memberInfo = type.GetMember(enumValue.ToString());
var member = memberInfo.FirstOrDefault(m => m.DeclaringType == type);
var attribute = Attribute.GetCustomAttribute(member, typeof(T), false);
return attribute is T ? (T)attribute : null;
}
#endregion
}
Model
The model in which we fill our values
public class MemberTypeModel : IDto
{
public string MemberAttributeName { get; set; }
public string MemberName { get; set; }
public int MemberValue { get; set; }
}
Enum
Our target is the enum
public enum MemberType
{
[FieldText("Yönetim Kurul Üyesi")]
BoardManager = 0,
[FieldText("Temsilci")]
Representative = 1,
[FieldText("Üye")]
Member = 2
}
Helper Method
The helper method we will use to fetch the custom attribute object
public T GetMemberCustomText<T>(MemberType memberType) where T : Attribute
{
var enumType = memberType.GetType();
var name = Enum.GetName(enumType, memberType);
return enumType.GetField(name).GetCustomAttributes(false).OfType<T>().SingleOrDefault();
}
Get Method
First we pull enum values and cast them to enum type. Then, with the Linq selection query we know;
MemberAttributeName field with helper method,
MemberName field with Enum.GetName method,
Casting the MemberValue field to an int type as well,
We fill it out and turn it into a list.
public List<MemberTypeModel> GetMemberTypes()
{
var memberTypes = Enum.GetValues(typeof(MemberType))
.Cast<MemberType>()
.Select(et => new MemberTypeModel
{
MemberAttributeName = GetMemberCustomText<FieldText>(et).Text,
MemberName = Enum.GetName(et.GetType(), et),
MemberValue = (int)et
}).ToList();
return memberTypes;
}
I implemented this extension method to get the description from enum values. It works for all kind of enums.
public static class EnumExtension
{
public static string ToDescription(this System.Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
}
Taking advantage of some of the newer C# language features, you can reduce the line count:
public static TAttribute GetEnumAttribute<TAttribute>(this Enum enumVal) where TAttribute : Attribute
{
var memberInfo = enumVal.GetType().GetMember(enumVal.ToString());
return memberInfo[0].GetCustomAttributes(typeof(TAttribute), false).OfType<TAttribute>().FirstOrDefault();
}
public static string GetEnumDescription(this Enum enumValue) => enumValue.GetEnumAttribute<DescriptionAttribute>()?.Description ?? enumValue.ToString();
Bryan Rowe and AdamCrawford thanks for your answers!
But if somebody need method for get Discription (not extension) you can use it:
string GetEnumDiscription(Enum EnumValue)
{
var type = EnumValue.GetType();
var memInfo = type.GetMember(EnumValue.ToString());
var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
return (attributes.Length > 0) ? ((DescriptionAttribute)attributes[0]).Description : null;
}
This extension method will obtain a string representation of an enum value using its XmlEnumAttribute. If no XmlEnumAttribute is present, it falls back to enum.ToString().
public static string ToStringUsingXmlEnumAttribute<T>(this T enumValue)
where T: struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an enumerated type");
}
string name;
var type = typeof(T);
var memInfo = type.GetMember(enumValue.ToString());
if (memInfo.Length == 1)
{
var attributes = memInfo[0].GetCustomAttributes(typeof(System.Xml.Serialization.XmlEnumAttribute), false);
if (attributes.Length == 1)
{
name = ((System.Xml.Serialization.XmlEnumAttribute)attributes[0]).Name;
}
else
{
name = enumValue.ToString();
}
}
else
{
name = enumValue.ToString();
}
return name;
}
And if you want the full list of names you can do something like
typeof (PharmacyConfigurationKeys).GetFields()
.Where(x => x.GetCustomAttributes(false).Any(y => typeof(DescriptionAttribute) == y.GetType()))
.Select(x => ((DescriptionAttribute)x.GetCustomAttributes(false)[0]).Description);
public enum DataFilters
{
[Display(Name= "Equals")]
Equals = 1,// Display Name and Enum Name are same
[Display(Name= "Does Not Equal")]
DoesNotEqual = 2, // Display Name and Enum Name are different
}
Now it will produce error in this case 1 "Equals"
public static string GetDisplayName(this Enum enumValue)
{
var enumMember = enumValue.GetType().GetMember(enumValue.ToString()).First();
return enumMember.GetCustomAttribute<DisplayAttribute>() != null ? enumMember.GetCustomAttribute<DisplayAttribute>().Name : enumMember.Name;
}
so if it is same return enum name rather than display name because
enumMember.GetCustomAttribute()
gets null if displayname and enum name are same.....
The NuGet package Enums.Net has good support for this:
var value = FunkyAttributesEnum.NameWithoutSpaces1;
string description = value.AsString(EnumFormat.Description); // => "Name With Spaces1"
The package is simple, intuitive, and complete.
It's type-safe and has cache to avoid recurring reflection.
The GitHub repository has more information, including the limitations of the native Enum and a demo of functionality:
Getting attributes;
Flag operations;
Enum formats (for ToString or to parse the enum from string);
Rich iteration of all values with Enums.GetMembers<MyEnum>();
Etc.
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..
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
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));
}
This is how I solved it without using custom helpers or extensions with .NET core 3.1.
Class
public enum YourEnum
{
[Display(Name = "Suryoye means Arameans")]
SURYOYE = 0,
[Display(Name = "Oromoye means Syriacs")]
OROMOYE = 1,
}
Razor
#using Enumerations
foreach (var name in Html.GetEnumSelectList(typeof(YourEnum)))
{
<h1>#name.Text</h1>
}
I have created an extension method that will return description of all the elements in an enum in C#.
public static List<string> GetAllEnumDescriptions(this Type enumType)
{
try
{
var enumList = Enum.GetValues(enumType).Cast<Enum>().ToList();
List<string> result = new List<string>();
foreach (var enumItem in enumList)
{
result.Add(enumItem.EnumDescription());
}
return result;
}
catch (Exception ex)
{
return new List<string>();
}
}
This method will add the description of the elements in an enum using the inbuilt EnumDescription() extension method.
I had this set of Enums for currency purpose
[DataContract]
public enum PaymentCurrency
{
/// <summary>
/// Canadian dollar.
/// </summary>
[EnumMember(Value = "CA$")]
CAD = 1,
}
When I want to display the particular item , for example CAD , I want it to show as "CA$" string . I tried it by assigning a value to it , it is not working and I haven't got much clue . Any ideas ? Thanks .
The value argument of the EnumMember attribute is there for serialization. Not display purposes. See MSDN Documentation.
To get at that value you'd have to serialize it then parse the XML.
Another way is to write your own helper method and take advantage of C# built-in DescriptionAttribute:
public enum PaymentCurrency
{
[DescriptionAttribute("CA$")]
CAD,
[DescriptionAttribute("US$")]
USD,
EURO
}
Then using your own helper methods in an EnumUtils class you could do this:
public class EnumUtils
{
public static string stringValueOf(Enum value)
{
var fi = value.GetType().GetField(value.ToString());
var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes( typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
else
{
return value.ToString();
}
}
public static object enumValueOf(string value, Type enumType)
{
string[] names = Enum.GetNames(enumType);
foreach (string name in names)
{
if (stringValueOf((Enum)Enum.Parse(enumType, name)).Equals(value))
{
return Enum.Parse(enumType, name);
}
}
throw new ArgumentException("The string is not a description or value of the specified enum.");
}
}
I have a db table that stores the following:
RuleID objectProperty ComparisonOperator TargetValue
1 age 'greater_than' 15
2 username 'equal' 'some_name'
3 tags 'hasAtLeastOne' 'some_tag some_tag2'
Now say I have a collection of these rules:
List<Rule> rules = db.GetRules();
Now I have an instance of a user also:
User user = db.GetUser(....);
How would I loop through these rules, and apply the logic and perform the comparisons etc?
if(user.age > 15)
if(user.username == "some_name")
Since the object's property like 'age' or 'user_name' is stored in the table, along with the comparison operater 'great_than' and 'equal', how could I possible do this?
C# is a statically typed language, so not sure how to go forward.
This snippet compiles the Rules into fast executable code (using Expression trees) and does not need any complicated switch statements:
(Edit : full working example with generic method)
public Func<User, bool> CompileRule(Rule r)
{
var paramUser = Expression.Parameter(typeof(User));
Expression expr = BuildExpr(r, paramUser);
// build a lambda function User->bool and compile it
return Expression.Lambda<Func<User, bool>>(expr, paramUser).Compile();
}
You can then write:
List<Rule> rules = new List<Rule> {
new Rule ("Age", "GreaterThan", "21"),
new Rule ( "Name", "Equal", "John"),
new Rule ( "Tags", "Contains", "C#" )
};
// compile the rules once
var compiledRules = rules.Select(r => CompileRule(r)).ToList();
public bool MatchesAllRules(User user)
{
return compiledRules.All(rule => rule(user));
}
Here is the implementation of BuildExpr:
Expression BuildExpr(Rule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.MemberName);
var tProp = typeof(User).GetProperty(r.MemberName).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary)) {
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 21'
return Expression.MakeBinary(tBinary, left, right);
} else {
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
Note that I used 'GreaterThan' instead of 'greater_than' etc. - this is because 'GreaterThan' is the .NET name for the operator, therefore we don't need any extra mapping.
If you need custom names you can build a very simple dictionary and just translate all operators before compiling the rules:
var nameMap = new Dictionary<string, string> {
{ "greater_than", "GreaterThan" },
{ "hasAtLeastOne", "Contains" }
};
The code uses the type User for simplicity. You can replace User with a generic type T to have a generic Rule compiler for any types of objects. Also, the code should handle errors, like unknown operator name.
Note that generating code on the fly was possible even before the Expression trees API was introduced, using Reflection.Emit. The method LambdaExpression.Compile() uses Reflection.Emit under the covers (you can see this using ILSpy).
Here is some code that compiles as is and does the job.
Basically use two dictionaries, one containing a mapping from operator names to boolean functions, and another containing a map from the property names of the User type to PropertyInfos used to invoke the property getter (if public).
You pass the User instance, and the three values from your table to the static Apply method.
class User
{
public int Age { get; set; }
public string UserName { get; set; }
}
class Operator
{
private static Dictionary<string, Func<object, object, bool>> s_operators;
private static Dictionary<string, PropertyInfo> s_properties;
static Operator()
{
s_operators = new Dictionary<string, Func<object, object, bool>>();
s_operators["greater_than"] = new Func<object, object, bool>(s_opGreaterThan);
s_operators["equal"] = new Func<object, object, bool>(s_opEqual);
s_properties = typeof(User).GetProperties().ToDictionary(propInfo => propInfo.Name);
}
public static bool Apply(User user, string op, string prop, object target)
{
return s_operators[op](GetPropValue(user, prop), target);
}
private static object GetPropValue(User user, string prop)
{
PropertyInfo propInfo = s_properties[prop];
return propInfo.GetGetMethod(false).Invoke(user, null);
}
#region Operators
static bool s_opGreaterThan(object o1, object o2)
{
if (o1 == null || o2 == null || o1.GetType() != o2.GetType() || !(o1 is IComparable))
return false;
return (o1 as IComparable).CompareTo(o2) > 0;
}
static bool s_opEqual(object o1, object o2)
{
return o1 == o2;
}
//etc.
#endregion
public static void Main(string[] args)
{
User user = new User() { Age = 16, UserName = "John" };
Console.WriteLine(Operator.Apply(user, "greater_than", "Age", 15));
Console.WriteLine(Operator.Apply(user, "greater_than", "Age", 17));
Console.WriteLine(Operator.Apply(user, "equal", "UserName", "John"));
Console.WriteLine(Operator.Apply(user, "equal", "UserName", "Bob"));
}
}
I built a rule engine that takes a different approach than you outlined in your question, but I think you will find it to be much more flexible than your current approach.
Your current approach seems to be focused on a single entity, "User", and your persistent rules identify "propertyname", "operator" and "value". My pattern, instead stores the C# code for a predicate (Func<T, bool>) in an "Expression" column in my database. In the current design, using code generation I am querying the "rules" from my database and compiling an assembly with "Rule" types, each with a "Test" method. Here is the signature for the interface that is implemented each Rule:
public interface IDataRule<TEntity>
{
/// <summary>
/// Evaluates the validity of a rule given an instance of an entity
/// </summary>
/// <param name="entity">Entity to evaluate</param>
/// <returns>result of the evaluation</returns>
bool Test(TEntity entity);
/// <summary>
/// The unique indentifier for a rule.
/// </summary>
int RuleId { get; set; }
/// <summary>
/// Common name of the rule, not unique
/// </summary>
string RuleName { get; set; }
/// <summary>
/// Indicates the message used to notify the user if the rule fails
/// </summary>
string ValidationMessage { get; set; }
/// <summary>
/// indicator of whether the rule is enabled or not
/// </summary>
bool IsEnabled { get; set; }
/// <summary>
/// Represents the order in which a rule should be executed relative to other rules
/// </summary>
int SortOrder { get; set; }
}
The "Expression" is compiled as the body of the "Test" method when the application first executes. As you can see the other columns in the table are also surfaced as first-class properties on the rule so that a developer has flexibility to create an experience for how the user gets notified of failure or success.
Generating an in-memory assembly is a 1-time occurrence during your application and you get a performance gain by not having to use reflection when evaluating your rules. Your expressions are checked at runtime as the assembly will not generate correctly if a property name is misspelled, etc.
The mechanics of creating an in-memory assembly are as follows:
Load your rules from the DB
iterate over the rules and for-each, using a StringBuilder and some string concatenation write the Text representing a class that inherits from IDataRule
compile using CodeDOM -- more info
This is actually quite simple because for the majority this code is property implementations and value initialization in the constructor. Besides that, the only other code is the Expression.
NOTE: there is a limitation that your expression must be .NET 2.0 (no lambdas or other C# 3.0 features) due to a limitation in CodeDOM.
Here is some sample code for that.
sb.AppendLine(string.Format("\tpublic class {0} : SomeCompany.ComponentModel.IDataRule<{1}>", className, typeName));
sb.AppendLine("\t{");
sb.AppendLine("\t\tprivate int _ruleId = -1;");
sb.AppendLine("\t\tprivate string _ruleName = \"\";");
sb.AppendLine("\t\tprivate string _ruleType = \"\";");
sb.AppendLine("\t\tprivate string _validationMessage = \"\";");
/// ...
sb.AppendLine("\t\tprivate bool _isenabled= false;");
// constructor
sb.AppendLine(string.Format("\t\tpublic {0}()", className));
sb.AppendLine("\t\t{");
sb.AppendLine(string.Format("\t\t\tRuleId = {0};", ruleId));
sb.AppendLine(string.Format("\t\t\tRuleName = \"{0}\";", ruleName.TrimEnd()));
sb.AppendLine(string.Format("\t\t\tRuleType = \"{0}\";", ruleType.TrimEnd()));
sb.AppendLine(string.Format("\t\t\tValidationMessage = \"{0}\";", validationMessage.TrimEnd()));
// ...
sb.AppendLine(string.Format("\t\t\tSortOrder = {0};", sortOrder));
sb.AppendLine("\t\t}");
// properties
sb.AppendLine("\t\tpublic int RuleId { get { return _ruleId; } set { _ruleId = value; } }");
sb.AppendLine("\t\tpublic string RuleName { get { return _ruleName; } set { _ruleName = value; } }");
sb.AppendLine("\t\tpublic string RuleType { get { return _ruleType; } set { _ruleType = value; } }");
/// ... more properties -- omitted
sb.AppendLine(string.Format("\t\tpublic bool Test({0} entity) ", typeName));
sb.AppendLine("\t\t{");
// #############################################################
// NOTE: This is where the expression from the DB Column becomes
// the body of the Test Method, such as: return "entity.Prop1 < 5"
// #############################################################
sb.AppendLine(string.Format("\t\t\treturn {0};", expressionText.TrimEnd()));
sb.AppendLine("\t\t}"); // close method
sb.AppendLine("\t}"); // close Class
Beyond this I did make a class I called "DataRuleCollection", which implemented ICollection>. This enabled me to create a "TestAll" capability and an indexer for executing a specific rule by name. Here are the implementations for those two methods.
/// <summary>
/// Indexer which enables accessing rules in the collection by name
/// </summary>
/// <param name="ruleName">a rule name</param>
/// <returns>an instance of a data rule or null if the rule was not found.</returns>
public IDataRule<TEntity, bool> this[string ruleName]
{
get { return Contains(ruleName) ? list[ruleName] : null; }
}
// in this case the implementation of the Rules Collection is:
// DataRulesCollection<IDataRule<User>> and that generic flows through to the rule.
// there are also some supporting concepts here not otherwise outlined, such as a "FailedRules" IList
public bool TestAllRules(User target)
{
rules.FailedRules.Clear();
var result = true;
foreach (var rule in rules.Where(x => x.IsEnabled))
{
result = rule.Test(target);
if (!result)
{
rules.FailedRules.Add(rule);
}
}
return (rules.FailedRules.Count == 0);
}
MORE CODE: There was a request for the code related to the Code Generation. I encapsulated the functionality in a class called 'RulesAssemblyGenerator' which I have included below.
namespace Xxx.Services.Utils
{
public static class RulesAssemblyGenerator
{
static List<string> EntityTypesLoaded = new List<string>();
public static void Execute(string typeName, string scriptCode)
{
if (EntityTypesLoaded.Contains(typeName)) { return; }
// only allow the assembly to load once per entityType per execution session
Compile(new CSharpCodeProvider(), scriptCode);
EntityTypesLoaded.Add(typeName);
}
private static void Compile(CodeDom.CodeDomProvider provider, string source)
{
var param = new CodeDom.CompilerParameters()
{
GenerateExecutable = false,
IncludeDebugInformation = false,
GenerateInMemory = true
};
var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
var root_Dir = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Bin");
param.ReferencedAssemblies.Add(path);
// Note: This dependencies list are included as assembly reference and they should list out all dependencies
// That you may reference in your Rules or that your entity depends on.
// some assembly names were changed... clearly.
var dependencies = new string[] { "yyyyyy.dll", "xxxxxx.dll", "NHibernate.dll", "ABC.Helper.Rules.dll" };
foreach (var dependency in dependencies)
{
var assemblypath = System.IO.Path.Combine(root_Dir, dependency);
param.ReferencedAssemblies.Add(assemblypath);
}
// reference .NET basics for C# 2.0 and C#3.0
param.ReferencedAssemblies.Add(#"C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll");
param.ReferencedAssemblies.Add(#"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll");
var compileResults = provider.CompileAssemblyFromSource(param, source);
var output = compileResults.Output;
if (compileResults.Errors.Count != 0)
{
CodeDom.CompilerErrorCollection es = compileResults.Errors;
var edList = new List<DataRuleLoadExceptionDetails>();
foreach (CodeDom.CompilerError s in es)
edList.Add(new DataRuleLoadExceptionDetails() { Message = s.ErrorText, LineNumber = s.Line });
var rde = new RuleDefinitionException(source, edList.ToArray());
throw rde;
}
}
}
}
If there are any other questions or comments or requests for further code samples, let me know.
Reflection is your most versatile answer. You have three columns of data, and they need to be treated in different ways:
Your field name. Reflection is the way to get the value from a coded field name.
Your comparison operator. There should be a limited number of these, so a case statement should handle them most easily. Especially as some of them ( has one or more of ) is slightly more complex.
Your comparison value. If these are all straight values then this is easy, although you will have divide the multiple entries up. However, you could also use reflection if they are field names too.
I would take an approach more like:
var value = user.GetType().GetProperty("age").GetValue(user, null);
//Thank you Rick! Saves me remembering it;
switch(rule.ComparisonOperator)
case "equals":
return EqualComparison(value, rule.CompareTo)
case "is_one_or_more_of"
return IsInComparison(value, rule.CompareTo)
etc. etc.
It gives you flexibility for adding more options for comparison. It also means that you can code within the Comparison methods any type validation that you might want, and make them as complex as you want. There is also the option here for the CompareTo to be evaluated as a recursive call back to another line, or as a field value, which could be done like:
return IsInComparison(value, EvaluateComparison(rule.CompareTo))
It all depends on the possibilities for the future....
If you only have a handful of properties and operators, the path of least of resistance is to just code up all the checks as special cases like this:
public bool ApplyRules(List<Rule> rules, User user)
{
foreach (var rule in rules)
{
IComparable value = null;
object limit = null;
if (rule.objectProperty == "age")
{
value = user.age;
limit = Convert.ToInt32(rule.TargetValue);
}
else if (rule.objectProperty == "username")
{
value = user.username;
limit = rule.TargetValue;
}
else
throw new InvalidOperationException("invalid property");
int result = value.CompareTo(limit);
if (rule.ComparisonOperator == "equal")
{
if (!(result == 0)) return false;
}
else if (rule.ComparisonOperator == "greater_than")
{
if (!(result > 0)) return false;
}
else
throw new InvalidOperationException("invalid operator");
}
return true;
}
If you have a lot of properties, you may find a table-driven approach more palatable. In that case you would create a static Dictionary that maps property names to delegates matching, say, Func<User, object>.
If you don't know the names of the properties at compile time, or you want to avoid special-cases for each property and don't want to use the table approach, you can use reflection to get properties. For example:
var value = user.GetType().GetProperty("age").GetValue(user, null);
But since TargetValue is probably a string, you'll need to take care to do type conversion from the rules table if necessary.
What about a data type orientated approach with an extention method:
public static class RoleExtension
{
public static bool Match(this Role role, object obj )
{
var property = obj.GetType().GetProperty(role.objectProperty);
if (property.PropertyType == typeof(int))
{
return ApplyIntOperation(role, (int)property.GetValue(obj, null));
}
if (property.PropertyType == typeof(string))
{
return ApplyStringOperation(role, (string)property.GetValue(obj, null));
}
if (property.PropertyType.GetInterface("IEnumerable<string>",false) != null)
{
return ApplyListOperation(role, (IEnumerable<string>)property.GetValue(obj, null));
}
throw new InvalidOperationException("Unknown PropertyType");
}
private static bool ApplyIntOperation(Role role, int value)
{
var targetValue = Convert.ToInt32(role.TargetValue);
switch (role.ComparisonOperator)
{
case "greater_than":
return value > targetValue;
case "equal":
return value == targetValue;
//...
default:
throw new InvalidOperationException("Unknown ComparisonOperator");
}
}
private static bool ApplyStringOperation(Role role, string value)
{
//...
throw new InvalidOperationException("Unknown ComparisonOperator");
}
private static bool ApplyListOperation(Role role, IEnumerable<string> value)
{
var targetValues = role.TargetValue.Split(' ');
switch (role.ComparisonOperator)
{
case "hasAtLeastOne":
return value.Any(v => targetValues.Contains(v));
//...
}
throw new InvalidOperationException("Unknown ComparisonOperator");
}
}
Than you can evaulate like this:
var myResults = users.Where(u => roles.All(r => r.Match(u)));
Although the most obvious way to answer the "How to implement a rule engine? (in C#)" question is to execute a given set of rules in sequence, this is in general considered as a naïve implementation (does not mean it does not work :-)
It seems it's "good enough" in your case because your problem seems more to be "how to run a set of rules in sequence", and the lambda/expression tree (Martin's answer) is certainly the most elegant way in that matter if you are equiped with recent C# versions.
However for more advanced scenarios, here is a link to the Rete Algorithm that is in fact implemented in many commercial rule engine systems, and another link to NRuler, an implementation of that algorithm in C#.
Martin's answer was quite good. I actually made a rules engine that has the same idea as his. And I was surprised that it's almost the same. I've included some of his code to somewhat improve it. Although I've made it to handle more complex rules.
You can look at Yare.NET
Or download it in Nuget
How about using the workflow rules engine?
You can execute Windows Workflow Rules without Workflow
see Guy Burstein's Blog: http://blogs.microsoft.co.il/blogs/bursteg/archive/2006/10/11/RuleExecutionWithoutWorkflow.aspx
and to programatically create your rules, see Stephen Kaufman's WebLog
http://blogs.msdn.com/b/skaufman/archive/2006/05/15/programmatically-create-windows-workflow-rules.aspx
I added implementation for and,or between rules
i added class RuleExpression that represent the root of a tree that can be leaf the is simple rule or can be and,or binary expressions there for they dont have rule and have expressions:
public class RuleExpression
{
public NodeOperator NodeOperator { get; set; }
public List<RuleExpression> Expressions { get; set; }
public Rule Rule { get; set; }
public RuleExpression()
{
}
public RuleExpression(Rule rule)
{
NodeOperator = NodeOperator.Leaf;
Rule = rule;
}
public RuleExpression(NodeOperator nodeOperator, List<RuleExpression> expressions, Rule rule)
{
this.NodeOperator = nodeOperator;
this.Expressions = expressions;
this.Rule = rule;
}
}
public enum NodeOperator
{
And,
Or,
Leaf
}
I have another class that compile the ruleExpression to one Func<T, bool>:
public static Func<T, bool> CompileRuleExpression<T>(RuleExpression ruleExpression)
{
//Input parameter
var genericType = Expression.Parameter(typeof(T));
var binaryExpression = RuleExpressionToOneExpression<T>(ruleExpression, genericType);
var lambdaFunc = Expression.Lambda<Func<T, bool>>(binaryExpression, genericType);
return lambdaFunc.Compile();
}
private static Expression RuleExpressionToOneExpression<T>(RuleExpression ruleExpression, ParameterExpression genericType)
{
if (ruleExpression == null)
{
throw new ArgumentNullException();
}
Expression finalExpression;
//check if node is leaf
if (ruleExpression.NodeOperator == NodeOperator.Leaf)
{
return RuleToExpression<T>(ruleExpression.Rule, genericType);
}
//check if node is NodeOperator.And
if (ruleExpression.NodeOperator.Equals(NodeOperator.And))
{
finalExpression = Expression.Constant(true);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.AndAlso(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression<T>(expression.Rule, genericType) :
RuleExpressionToOneExpression<T>(expression, genericType));
});
return finalExpression;
}
//check if node is NodeOperator.Or
else
{
finalExpression = Expression.Constant(false);
ruleExpression.Expressions.ForEach(expression =>
{
finalExpression = Expression.Or(finalExpression, expression.NodeOperator.Equals(NodeOperator.Leaf) ?
RuleToExpression<T>(expression.Rule, genericType) :
RuleExpressionToOneExpression<T>(expression, genericType));
});
return finalExpression;
}
}
public static BinaryExpression RuleToExpression<T>(Rule rule, ParameterExpression genericType)
{
try
{
Expression value = null;
//Get Comparison property
var key = Expression.Property(genericType, rule.ComparisonPredicate);
Type propertyType = typeof(T).GetProperty(rule.ComparisonPredicate).PropertyType;
//convert case is it DateTimeOffset property
if (propertyType == typeof(DateTimeOffset))
{
var converter = TypeDescriptor.GetConverter(propertyType);
value = Expression.Constant((DateTimeOffset)converter.ConvertFromString(rule.ComparisonValue));
}
else
{
value = Expression.Constant(Convert.ChangeType(rule.ComparisonValue, propertyType));
}
BinaryExpression binaryExpression = Expression.MakeBinary(rule.ComparisonOperator, key, value);
return binaryExpression;
}
catch (FormatException)
{
throw new Exception("Exception in RuleToExpression trying to convert rule Comparison Value");
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
I have created a package for a rich and high performance rule engine written in dotnet, check out this repo for more info.
Once installed, you can use it as simple as:
var engine = new RulesService<TestModel>(new RulesCompiler(), new LazyCache.Mocks.MockCachingService());
var matchingRules = engine.GetMatchingRules(
new TestModel { NumericField = 5 },
new[] {
new RulesConfig {
Id = Guid.NewGuid(),
RulesOperator = Rule.InterRuleOperatorType.And,
RulesGroups = new RulesGroup[] {
new RulesGroup {
RulesOperator = Rule.InterRuleOperatorType.And,
Rules = new[] {
new Rule {
ComparisonOperator = Rule.ComparisonOperatorType.Equal,
ComparisonValue = 5.ToString(),
ComparisonPredicate = nameof(TestModel.NumericField)
}
}
}
}
}
});
I want to add Microsoft's Rules Engine library for future visits;
https://github.com/microsoft/RulesEngine
I have the following enum:
public enum Urgency {
VeryHigh = 1,
High = 2,
Routine = 4
}
I can fetch an enum "value" as string like this:
((int)Urgency.Routine).ToString() // returns "4"
Note: This is different from:
Urgency.Routine.ToString() // returns "Routine"
(int)Urgency.Routine // returns 4
Is there a way I can create an extension class, or a static utliity class, that would provide some syntactical sugar? :)
You should just be able to use the overloads of Enums ToString method to give it a format string, this will print out the value of the enum as a string.
public static class Program
{
static void Main(string[] args)
{
var val = Urgency.High;
Console.WriteLine(val.ToString("D"));
}
}
public enum Urgency
{
VeryHigh = 1,
High = 2,
Low = 4
}
In order to achieve more "human readable" descriptions for enums (e.g. "Very High" rather than "VeryHigh" in your example) I have decorated enum values with attribute as follows:
public enum MeasurementType
{
Each,
[DisplayText("Lineal Metres")]
LinealMetre,
[DisplayText("Square Metres")]
SquareMetre,
[DisplayText("Cubic Metres")]
CubicMetre,
[DisplayText("Per 1000")]
Per1000,
Other
}
public class DisplayText : Attribute
{
public DisplayText(string Text)
{
this.text = Text;
}
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
}
Then, used an extension method like this:
public static string ToDescription(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(DisplayText),
false);
if (attrs != null && attrs.Length > 0)
return ((DisplayText)attrs[0]).Text;
}
return en.ToString();
}
You can then just call myEnum.ToDescription() in order to display your enum as more readable text.
If you want to just deal with this enum, use Mark Byer's solution.
For a more general solution:
public static string NumberString(this Enum enVal)
{
return Convert.ToDecimal(enVal).ToString("0");
}
Converting to decimal means you don't need to deal with the 8 different allowed underlying integral types explicitly, as all of them convert losslessly to decimal but not to each other (ulong and long don't convert losslessly between each other but both can handle all the rest). Doing that would probably be faster (esp. if you pick well in your order of comparison), but a lot more verbose for relatively little gain.
Edit:
The above isn't as good as Frankentosh's though, Frankentosh saw through the question to the real problem and solves it very eloquently.
Great stuff ... I have now added an extension method to my project
public static class EnumExtensions
{
public static string NumberString(this Enum enVal)
{
return enVal.ToString("D");
}
}
Now I can get the int value - as a string - by calling Urgency.Routine.NumberString(); Thanks to Frankentosh and Jon :)
a simple approach
((Urgency)4).ToString() // returns "Routine"
You can write an extension method for your specific type:
public static class UrgencyExtension
{
public static string ToIntegerString(this Urgency u)
{
return ((int)u).ToString();
}
}
Use as follows:
Urgency u = Urgency.Routine;
string s = u.ToIntegerString();
How about a little reflection? Should work with all underlying types.
public static class EnumTools
{
public static string ToRawValueString(this Enum e)
{
return e
.GetType()
.GetFields(BindingFlags.Public | BindingFlags.Static)
.First(f => f.Name==e.ToString())
.GetRawConstantValue()
.ToString();
}
}
Then:
Console.WriteLine(Urgency.High.ToRawValueString()); //Writes "2"
If you wanted, you could make the extension method work for all enums:
public static string ToValueString(this Enum enumValue)
{
if (enumValue.GetType().GetEnumUnderlyingType() == typeof(int))
return ((int)(object)enumValue).ToString();
else if (enumValue.GetType().GetEnumUnderlyingType() == typeof(byte))
return ((byte)(object)enumValue).ToString();
...
}