如何为枚举提供用户友好的名称?

我有一个类似的枚举

Enum Complexity { NotSoComplex, LittleComplex, Complex, VeryComplex } 

我想在下拉列表中使用它,但不希望在列表中看到这样的Camel名称(对用户来说看起来很奇怪)。 相反,我希望有正常的措辞,如不那么复杂的小复杂(等)

此外,我的应用程序是多语言,我希望能够显示这些字符串本地化,我使用帮助器,TranslationHelper(字符串strID),它给我一个字符串ID的本地化版本。

我有一个工作的解决方案,但不是很优雅:我为枚举创建一个帮助类,其中一个成员Complexity和ToString()被覆盖,如下所示(代码简化)

 public class ComplexityHelper { public ComplexityHelper(Complexity c, string desc) { m_complex = c; m_desc=desc; } public Complexity Complexity { get { ... } set {...} } public override ToString() { return m_desc; } //Then a static field like this private static List m_cxList = null; // and method that returns the status lists to bind to DataSource of lists public static List GetComplexities() { if (m_cxList == null) { string[] list = TranslationHelper.GetTranslation("item_Complexities").Split(','); Array listVal = Enum.GetValues(typeof(Complexities)); if (list.Length != listVal.Length) throw new Exception("Invalid Complexities translations (item_Complexities)"); m_cxList = new List(); for (int i = 0; i < list.Length; i++) { Complexity cx = (ComplexitylistVal.GetValue(i); ComplexityHelper ch = new ComplexityHelper(cx, list[i]); m_cxList.Add(ch); } } return m_cxList; } } 

虽然可行,但我对此并不满意,因为我必须对我需要在选项列表中使用的各种枚举进行类似编码。

有没有人建议更简单或更通用的解决方案?

谢谢波格丹

基本友好名称

使用Description属性: *

 enum MyEnum { [Description("This is black")] Black, [Description("This is white")] White } 

和枚举的一个方便的扩展方法:

 public static string GetDescription(this Enum value) { FieldInfo field = value.GetType().GetField(value.ToString()); object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true); if(attribs.Length > 0) { return ((DescriptionAttribute)attribs[0]).Description; } return string.Empty; } 

像这样使用:

 MyEnum val = MyEnum.Black; Console.WriteLine(val.GetDescription()); //writes "This is black" 

(注意这对位标志不起作用……)

用于本地化

.NET中有一个完善的模式,用于处理每个字符串值的多种语言 – 使用资源文件 ,并展开扩展方法以从资源文件中读取:

 public static string GetDescription(this Enum value) { FieldInfo field = value.GetType().GetField(value.ToString()); object[] attribs = field.GetCustomAttributes(typeof(DescriptionAttribute), true)); if(attribs.Length > 0) { string message = ((DescriptionAttribute)attribs[0]).Description; return resourceMgr.GetString(message, CultureInfo.CurrentCulture); } return string.Empty; } 

任何时候我们都可以利用现有的BCLfunction来实现我们想要的,这绝对是探索的第一条路线。 这最大限度地降低了复杂性,并使用了许多其

把它们放在一起

为了将其绑定到DropDownList,我们可能希望跟踪控件中的实际枚举值,并将已翻译的友好名称限制为可视糖。 我们可以通过在列表中使用匿名类型和DataField属性来实现:

  myDDL.DataSource = Enum.GetValues(typeof(MyEnum)).OfType().Select( val => new { Description = val.GetDescription(), Value = val.ToString() }); myDDL.DataBind(); 

让我们分解一下DataSource行:

  • 首先我们调用Enum.GetValues(typeof(MyEnum)) ,它为我们提供了一个松散类型的值Array
  • 接下来我们调用OfType() ,它将数组转换为IEnumerable
  • 然后我们调用Select()并提供一个lambda,它使用两个字段Description和Value来投影一个新对象。

DataTextField和DataValueField属性在数据绑定时反复评估,因此它们将在DataItem上搜索具有匹配名称的字段。

请注意,在主要文章中,作者编写了自己的DescriptionAttribute类,这是不必要的,因为.NET的标准库中已经存在这种类。

在其他答案中使用属性是一种很好的方法,但如果您只想使用枚举值中的文本,则以下代码将根据值的驼峰大小分割:

 public static string GetDescriptionOf(Enum enumType) { Regex capitalLetterMatch = new Regex("\\B[AZ]", RegexOptions.Compiled); return capitalLetterMatch.Replace(enumType.ToString(), " $&"); } 

调用GetDescriptionOf(Complexity.NotSoComplex)将返回Not So Complex 。 这可以与任何枚举值一起使用。

为了使它更有用,你可以使它成为一个扩展方法:

 public static string ToFriendlyString(this Enum enumType) { Regex capitalLetterMatch = new Regex("\\B[AZ]", RegexOptions.Compiled); return capitalLetterMatch.Replace(enumType.ToString(), " $&"); } 

你现在使用Complexity.NotSoComplex.ToFriendlyString()调用它来返回Not So Complex


编辑 :刚才注意到在你的问题中你提到你需要本地化文本。 在这种情况下,我使用一个属性来包含一个键来查找本地化的值,但如果找不到本地化的文本,则默认使用友好的字符串方法作为最后的手段。 您可以定义这样的枚举:

 enum Complexity { [LocalisedEnum("Complexity.NotSoComplex")] NotSoComplex, [LocalisedEnum("Complexity.LittleComplex")] LittleComplex, [LocalisedEnum("Complexity.Complex")] Complex, [LocalisedEnum("Complexity.VeryComplex")] VeryComplex } 

您还需要以下代码:

 [AttributeUsage(AttributeTargets.Field, AllowMultiple=false, Inherited=true)] public class LocalisedEnum : Attribute { public string LocalisationKey{get;set;} public LocalisedEnum(string localisationKey) { LocalisationKey = localisationKey; } } public static class LocalisedEnumExtensions { public static string ToLocalisedString(this Enum enumType) { // default value is the ToString(); string description = enumType.ToString(); try { bool done = false; MemberInfo[] memberInfo = enumType.GetType().GetMember(enumType.ToString()); if (memberInfo != null && memberInfo.Length > 0) { object[] attributes = memberInfo[0].GetCustomAttributes(typeof(LocalisedEnum), false); if (attributes != null && attributes.Length > 0) { LocalisedEnum descriptionAttribute = attributes[0] as LocalisedEnum; if (description != null && descriptionAttribute != null) { string desc = TranslationHelper.GetTranslation(descriptionAttribute.LocalisationKey); if (desc != null) { description = desc; done = true; } } } } if (!done) { Regex capitalLetterMatch = new Regex("\\B[AZ]", RegexOptions.Compiled); description = capitalLetterMatch.Replace(enumType.ToString(), " $&"); } } catch { description = enumType.ToString(); } return description; } } 

要获得本地化描述,您可以调用:

 Complexity.NotSoComplex.ToLocalisedString() 

这有几个后备案例:

  • 如果枚举具有已定义的LocalisedEnum属性,则它将使用该键来查找已翻译的文本
  • 如果枚举已定义LocalisedEnum属性但未找到本地化文本,则默认使用camel-case拆分方法
  • 如果枚举没有定义LocalisedEnum属性,它将使用camel-case拆分方法
  • 在任何错误时,它默认为枚举值的ToString

我使用以下课程

  public class EnumUtils { ///  /// Reads and returns the value of the Description Attribute of an enumeration value. ///  /// The enumeration value whose Description attribute you wish to have returned. /// The string value portion of the Description attribute. public static string StringValueOf(Enum value) { FieldInfo fi = value.GetType().GetField(value.ToString()); DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); if (attributes.Length > 0) { return attributes[0].Description; } else { return value.ToString(); } } ///  /// Returns the Enumeration value that has a given Description attribute. ///  /// The Description attribute value. /// The type of enumeration in which to search. /// The enumeration value that matches the Description value provided. /// Thrown when the specified Description value is not found with in the provided Enumeration Type. 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."); } 

其中读取了一个名为description的属性

 public enum PuppyType { [Description("Cute Puppy")] CutePuppy = 0, [Description("Silly Puppy")] SillyPuppy } 

谢谢大家的所有答案。 最后,我使用了Rex M和adrianbanks的组合,并添加了我自己的改进,以简化与ComboBox的绑定。

需要进行更改,因为在处理代码时,我意识到有时我需要能够从组合中排除一个枚举项。 例如

 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 } 

因此,有时我希望选项列表显示除AllComplexities之外的所有内容(添加 – 编辑模块)以及其他显示所有内容的时间(在filter中)

这是我做的:

  1. 我创建了一个扩展方法,它使用Description Attribute作为本地化查找键。 如果缺少Description属性,我将查找本地化密钥创建为EnumName_ EnumValue。 最后,如果缺少翻译,我只需将基于camelcase的枚举名称拆分为单独的单词,如adrianbanks所示。 BTW,TranslationHelper是resourceMgr.GetString(…)的包装器

完整代码如下所示

 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[AZ]", RegexOptions.Compiled); enumDesc = capitalLetterMatch.Replace(value.ToString(), " $&"); } } catch (Exception) { // if any error, fallback to string value enumDesc = value.ToString(); } return enumDesc; } 

我创建了一个基于Enum的通用助手类,它允许将枚举轻松绑定到DataSource

 public class LocalizableEnum { ///  /// Column names exposed by LocalizableEnum ///  public class ColumnNames { public const string ID = "EnumValue"; public const string EntityValue = "EnumDescription"; } } public class LocalizableEnum { 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(); } } } 

然后我创建了一个返回List>的通用静态方法,如下所示

 public static List> GetEnumList(object excludeMember) { List> 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>(); 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 enumMember = new LocalizableEnum(currentVal); list.Add(enumMember); } } } return list; } 

以及包含所有成员的返回列表的包装器

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

现在让我们把所有东西放在一起并绑定到实际的组合:

 // 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(); 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.AllComplexities); comboComplexity.SelectedValue = Complexity.VeryComplex; // set default value 

要读取所选值并使用它,我使用如下代码

 Complexity selComplexity = (Complexity)comboComplexity.SelectedValue;