Can My Enums Have Friendly Names

Can my enums have friendly names?

Enum value names must follow the same naming rules as all identifiers in C#, therefore only first name is correct.

How to have userfriendly names for enumerations?

Thank you all for all answers.
Finally I used a combination from Rex M and adrianbanks, and added my own improvements, to simplify the binding to ComboBox.

The changes were needed because, while working on the code, I realized sometimes I need to be able to exclude one enumeration item from the combo.
E.g.

Enum Complexity
{
// this will be used in filters,
// but not in module where I have to assign Complexity to a field
AllComplexities,
NotSoComplex,
LittleComplex,
Complex,
VeryComplex
}

So sometimes I want the picklist to show all but AllComplexities (in add - edit modules) and other time to show all (in filters)

Here's what I did:

  1. I created a extension method, that uses Description Attribute as localization lookup key. If Description attribute is missing, I create the lookup localization key as EnumName_
    EnumValue. Finally, if translation is missing I just split enum name based on camelcase to separate words as shown by adrianbanks. BTW, TranslationHelper is a wrapper around resourceMgr.GetString(...)

The full code is shown below

public static string GetDescription(this System.Enum value)
{
string enumID = string.Empty;
string enumDesc = string.Empty;
try
{
// try to lookup Description attribute
FieldInfo field = value.GetType().GetField(value.ToString());
object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true);
if (attribs.Length > 0)
{
enumID = ((DescriptionAttribute)attribs[0]).Description;
enumDesc = TranslationHelper.GetTranslation(enumID);
}
if (string.IsNullOrEmpty(enumID) || TranslationHelper.IsTranslationMissing(enumDesc))
{
// try to lookup translation from EnumName_EnumValue
string[] enumName = value.GetType().ToString().Split('.');
enumID = string.Format("{0}_{1}", enumName[enumName.Length - 1], value.ToString());
enumDesc = TranslationHelper.GetTranslation(enumID);
if (TranslationHelper.IsTranslationMissing(enumDesc))
enumDesc = string.Empty;
}

// try to format CamelCase to proper names
if (string.IsNullOrEmpty(enumDesc))
{
Regex capitalLetterMatch = new Regex("\\B[A-Z]", RegexOptions.Compiled);
enumDesc = capitalLetterMatch.Replace(value.ToString(), " $&");
}
}
catch (Exception)
{
// if any error, fallback to string value
enumDesc = value.ToString();
}

return enumDesc;
}

I created a generic helper class based on Enum, which allow to bind the enum easily to DataSource

public class LocalizableEnum
{
/// <summary>
/// Column names exposed by LocalizableEnum
/// </summary>
public class ColumnNames
{
public const string ID = "EnumValue";
public const string EntityValue = "EnumDescription";
}
}

public class LocalizableEnum<T>
{

private T m_ItemVal;
private string m_ItemDesc;

public LocalizableEnum(T id)
{
System.Enum idEnum = id as System.Enum;
if (idEnum == null)
throw new ArgumentException(string.Format("Type {0} is not enum", id.ToString()));
else
{
m_ItemVal = id;
m_ItemDesc = idEnum.GetDescription();
}
}

public override string ToString()
{
return m_ItemDesc;
}

public T EnumValue
{
get { return m_ID; }
}

public string EnumDescription
{
get { return ToString(); }
}

}

Then I created a generic static method that returns a List>, as below

public static List<LocalizableEnum<T>> GetEnumList<T>(object excludeMember)
{
List<LocalizableEnum<T>> list =null;
Array listVal = System.Enum.GetValues(typeof(T));
if (listVal.Length>0)
{
string excludedValStr = string.Empty;
if (excludeMember != null)
excludedValStr = ((T)excludeMember).ToString();

list = new List<LocalizableEnum<T>>();
for (int i = 0; i < listVal.Length; i++)
{
T currentVal = (T)listVal.GetValue(i);
if (excludedValStr != currentVal.ToString())
{
System.Enum enumVal = currentVal as System.Enum;
LocalizableEnum<T> enumMember = new LocalizableEnum<T>(currentVal);
list.Add(enumMember);
}
}
}
return list;
}

and a wrapper to return list with all members

public static List<LocalizableEnum<T>> GetEnumList<T>()
{
return GetEnumList<T>(null);
}

Now let's put all things together and bind to actual combo:

// in module where we want to show items with all complexities
// or just filter on one complexity

comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>();
comboComplexity.SelectedValue = Complexity.AllComplexities;

// ....
// and here in edit module where we don't want to see "All Complexities"
comboComplexity.DisplayMember = LocalizableEnum.ColumnNames.EnumValue;
comboComplexity.ValueMember = LocalizableEnum.ColumnNames.EnumDescription;
comboComplexity.DataSource = EnumHelper.GetEnumList<Complexity>(Complexity.AllComplexities);
comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value

To read selected the value and use it, I use code as below

Complexity selComplexity = (Complexity)comboComplexity.SelectedValue;

Using enum friendly Name

What you could do is make a readonly property on your model from which you return your FriendlyName

public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
public int Hour { get; set; }
public MyDayOfWeek Days { get; set; }

public string DaysFriendlyName
{
get
{
switch(this.Days)
{
case MyDayOfWeek.SunTilFir:
return "Sunday until Friday ";
}
return "Horrible Failure!!";
}
}
}

Probably even better to not implement the switch in the body of your readonly property, but maybe make an extensions method for it? So your body will look like this:

public string DaysFriendlyName
{
get
{
return this.Days.ToFriendlyName();
}
}

And the extension method:

namespace UniPro.Models
{
public static class DaysEnumExtensions
{
public static string ToFriendlyName(this MyDayOfWeek days)
{
switch (days)
{
//....
}
}
}
}

The nice part of the extension method would be that you could loose the readonly property and bind directly to the extension method from your razor view (put in the correct 'using' in your razor view for this to work).

Friendly Enum Strings With Flags Attribute

You can write a method just as in the answer you link, but with support for flag enums, and return a comma seperated string, something like:

public static string GetDescription(Enum value)
{
Type type = value.GetType();
var values = Enum.GetValues(type);
var setValues = new List<Enum>();
foreach(var enumValue in values)
{
if (value.HasFlag((Enum)enumValue))
setValues.Add((Enum)enumValue);
}
var stringList = new List<string>();
foreach (var singleValue in setValues)
{
var name = Enum.GetName(type, singleValue);
if (name != null)
{
FieldInfo field = type.GetField(name);
if (field != null)
{
DescriptionAttribute attr =
Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) as DescriptionAttribute;
if (attr != null)
{
stringList.Add(attr.Description);
}
}
}
}
return string.Join(",", stringList.ToArray());
}

not the cleanest code, but you get the idea, only keep in my mind, that it wont work as expected for enums that are not flags - just throwing an idea.

Enum ToString with user friendly strings

I use the Description attribute from the System.ComponentModel namespace. Simply decorate the enum:

private enum PublishStatusValue
{
[Description("Not Completed")]
NotCompleted,
Completed,
Error
};

Then use this code to retrieve it:

public static string GetDescription<T>(this T enumerationValue)
where T : struct
{
Type type = enumerationValue.GetType();
if (!type.IsEnum)
{
throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
}

//Tries to find a DescriptionAttribute for a potential friendly name
//for the enum
MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
if (memberInfo != null && memberInfo.Length > 0)
{
object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

if (attrs != null && attrs.Length > 0)
{
//Pull out the description value
return ((DescriptionAttribute)attrs[0]).Description;
}
}
//If we have no description attribute, just return the ToString of the enum
return enumerationValue.ToString();
}

Can a C# enum entry have a hyphen in the name

No, a hyphen is not allowed.

Identifiers

You could obviously replace the hyphen with an underscore, but as @benPearce suggested, CamelCase would be a better choice, and in line with most C# coding standards.



Related Topics



Leave a reply



Submit