Enum ToString with user friendly strings
我的枚举由以下值组成:
1 2 3 4 5 | private enum PublishStatusses{ NotCompleted, Completed, Error }; |
不过,我希望能够以用户友好的方式输出这些值。我不需要再从一个字符串到另一个值。
我使用扩展方法执行此操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public enum ErrorLevel { None, Low, High, SoylentGreen } public static class ErrorLevelExtensions { public static string ToFriendlyString(this ErrorLevel me) { switch(me) { case ErrorLevel.None: return"Everything is OK"; case ErrorLevel.Low: return"SNAFU, if you know what I mean."; case ErrorLevel.High: return"Reaching TARFU levels"; case ErrorLevel.SoylentGreen: return"ITS PEOPLE!!!!"; default: return"Get your damn dirty hands off me you FILTHY APE!"; } } } |
我使用system.componentModel名称空间中的
1 2 3 4 5 6 7 | private enum PublishStatusValue { [Description("Not Completed")] NotCompleted, Completed, Error }; |
然后使用此代码检索它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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(); } |
也许我遗漏了一些东西,但是enum.getname出了什么问题?
1 2 3 4 | public string GetName(PublishStatusses value) { return Enum.GetName(typeof(PublishStatusses), value) } |
编辑:对于用户友好的字符串,需要通过.resource来完成国际化/本地化,并且可以说,使用基于枚举键的固定键比使用相同的decorator属性要好。
我创建了一个反向扩展方法,将描述转换回枚举值:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static T ToEnumValue<T>(this string enumerationDescription) where T : struct { var type = typeof(T); if (!type.IsEnum) throw new ArgumentException("ToEnumValue<T>(): Must be of enum type","T"); foreach (object val in System.Enum.GetValues(type)) if (val.GetDescription<T>() == enumerationDescription) return (T)val; throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum" + type.Name,"enumerationDescription"); } |
这里最简单的解决方案是使用自定义扩展方法(至少在.NET 3.5中-您可以将其转换为早期框架版本的静态助手方法)。
1 2 3 4 5 6 7 8 | public static string ToCustomString(this PublishStatusses value) { switch(value) { // Return string depending on value. } return null; } |
我假设您要返回的不是枚举值的实际名称(只需调用ToString就可以得到)。
另一个帖子是Java。不能将方法放在c_中的枚举中。
就这样做:
1 2 | PublishStatusses status = ... String s = status.ToString(); |
如果要为枚举值使用不同的显示值,可以使用属性和反射。
其他一些避免类/引用类型的更原始的选项:
- 阵列法
- 嵌套结构方法
阵列法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | private struct PublishStatusses { public static string[] Desc = { "Not Completed", "Completed", "Error" }; public enum Id { NotCompleted = 0, Completed, Error }; } |
用法
1 | string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed]; |
嵌套结构方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | private struct PublishStatusses { public struct NotCompleted { public const int Id = 0; public const string Desc ="Not Completed"; } public struct Completed { public const int Id = 1; public const string Desc ="Completed"; } public struct Error { public const int Id = 2; public const string Desc ="Error"; } } |
用法
1 2 | int id = PublishStatusses.NotCompleted.Id; string desc = PublishStatusses.NotCompleted.Desc; |
更新(2018年9月3日)
扩展方法和上面的第一种技术的混合。
我更喜欢将枚举定义为它们"所属"的位置(最接近它们的源,而不是在一些通用的全局命名空间中)。
1 2 3 4 5 6 7 8 9 10 11 12 | namespace ViewModels { public class RecordVM { //public enum Enum { Minutes, Hours } public struct Enum { public enum Id { Minutes, Hours } public static string[] Name = {"Minute(s)","Hour(s)" }; } } } |
扩展方法似乎适用于公共区域,而枚举的"本地化"定义现在使扩展方法更加冗长。
1 2 3 4 5 6 7 8 9 10 | namespace Common { public static class EnumExtensions { public static string Name(this RecordVM.Enum.Id id) { return RecordVM.Enum.Name[(int)id]; } } } |
枚举及其扩展方法的用法示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | namespace Views { public class RecordView { private RecordDataFieldList<string, string> _fieldUnit; public RecordView() { _fieldUnit.List = new IdValueList<string, string> { new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()), new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name()) }; } private void Update() { RecordVM.Enum.Id eId = DetermineUnit(); _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value; } } } |
注意:我实际上决定取消
1 2 3 4 5 6 7 8 9 10 11 12 | namespace ViewModels { public class RecordVM { public enum Enum { Minutes, Hours } //public struct Enum //{ // public enum Id { Minutes, Hours } // public static string[] Name = {"Minute(s)","Hour(s)" }; //} } } |
公用计算机
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //namespace Common namespace ViewModels { public static class EnumExtensions { public static string Name(this RecordVM.Enum id) { //return RecordVM.Enum.Name[(int)id]; switch (id) { case RecordVM.Enum.Minutes: return"Minute(s)"; case RecordVM.Enum.Hours: return"Hour(s)"; default: return null; } } } } |
枚举及其扩展方法的用法示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | namespace Views { public class RecordView { private RecordDataFieldList<string, string> _fieldUnit public RecordView() { _fieldUnit.List = new IdValueList<string, string> { new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()), new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name()) }; } private void Update() { RecordVM.Enum eId = DetermineUnit(); _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value; } } } |
最简单的方法就是将这个扩展类包含到项目中,它将与项目中的任何枚举一起工作:
1 2 3 4 5 6 7 | public static class EnumExtensions { public static string ToFriendlyString(this Enum code) { return Enum.GetName(code.GetType(), code); } } |
用途:
1 2 3 4 5 6 | enum ExampleEnum { Demo = 0, Test = 1, Live = 2 } |
…
1 2 | ExampleEnum ee = ExampleEnum.Live; Console.WriteLine(ee.ToFriendlyString()); |
可以使用Humanizer包和Humanize枚举可能性。斧头:
1 2 3 4 5 6 7 | enum PublishStatusses { [Description("Custom description")] NotCompleted, AlmostCompleted, Error }; |
然后可以直接对枚举使用
1 2 3 4 5 | var st1 = PublishStatusses.NotCompleted; var str1 = st1.Humanize(); // will result in Custom description var st2 = PublishStatusses.AlmostCompleted; var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly) |
关于RayBooysen,代码中有一个bug:Enum-ToString和用户友好的字符串
您需要考虑枚举值上的多个属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static string GetDescription<T>(this object 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 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null) { //Pull out the description value return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description; } } //If we have no description attribute, just return the ToString of the enum return enumerationValue.ToString(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public enum MyEnum { [Description("Option One")] Option_One } public static string ToDescriptionString(this Enum This) { Type type = This.GetType(); string name = Enum.GetName(type, This); MemberInfo member = type.GetMembers() .Where(w => w.Name == name) .FirstOrDefault(); DescriptionAttribute attribute = member != null ? member.GetCustomAttributes(true) .Where(w => w.GetType() == typeof(DescriptionAttribute)) .FirstOrDefault() as DescriptionAttribute : null; return attribute != null ? attribute.Description : name; } |
不要使用枚举,而是使用静态类。
代替
1 2 3 4 5 | private enum PublishStatuses{ NotCompleted, Completed, Error }; |
具有
1 2 3 4 5 | private static class PublishStatuses{ public static readonly string NotCompleted ="Not Completed"; public static readonly string Completed ="Completed"; public static readonly string Error ="Error"; }; |
像这样用
1 | PublishStatuses.NotCompleted; //"Not Completed" |
使用顶级"扩展方法"解决方案的问题:
私有枚举通常在另一个类中使用。扩展方法解决方案在那里无效,因为它必须在自己的类中。此解决方案可以是私有的,并嵌入到另一个类中。
以上建议的总结与示例一目了然:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | namespace EnumExtensions { using System; using System.Reflection; public class TextAttribute : Attribute { public string Text; public TextAttribute( string text ) { Text = text; }//ctor }// class TextAttribute public static class EnumExtender { public static string ToText( this Enum enumeration ) { MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() ); if ( memberInfo != null && memberInfo.Length > 0 ) { object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute), false ); if ( attributes != null && attributes.Length > 0 ) { return ( (TextAttribute)attributes[ 0 ] ).Text; } }//if return enumeration.ToString(); }//ToText }//class EnumExtender }//namespace |
用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using System; using EnumExtensions; class Program { public enum Appearance { [Text("left-handed" ) ] Left, [Text("right-handed" ) ] Right, }//enum static void Main( string[] args ) { var appearance = Appearance.Left; Console.WriteLine( appearance.ToText() ); }//Main }//class |
我碰巧是一个vb.net迷,所以这里是我的版本,将DescriptionAttribute方法与扩展方法结合在一起。首先,结果是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | Imports System.ComponentModel ' For <Description> Module Module1 ''' <summary> ''' An Enum type with three values and descriptions ''' </summary> Public Enum EnumType <Description("One")> V1 = 1 ' This one has no description V2 = 2 <Description("Three")> V3 = 3 End Enum Sub Main() ' Description method is an extension in EnumExtensions For Each v As EnumType In [Enum].GetValues(GetType(EnumType)) Console.WriteLine("Enum {0} has value {1} and description {2}", v, CInt(v), v.Description ) Next ' Output: ' Enum V1 has value 1 and description One ' Enum V2 has value 2 and description V2 ' Enum V3 has value 3 and description Three End Sub End Module |
基本内容:一个名为EnumType的枚举,有三个值v1、v2和v3。"magic"发生在sub main()中的console.writeline调用中,其中最后一个参数只是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | Option Strict On Option Explicit On Option Infer Off Imports System.Runtime.CompilerServices Imports System.Reflection Imports System.ComponentModel Module EnumExtensions Private _Descriptions As New Dictionary(Of String, String) ''' <summary> ''' This extension method adds a Description method ''' to all enum members. The result of the method is the ''' value of the Description attribute if present, else ''' the normal ToString() representation of the enum value. ''' </summary> <Extension> Public Function Description(e As [Enum]) As String ' Get the type of the enum Dim enumType As Type = e.GetType() ' Get the name of the enum value Dim name As String = e.ToString() ' Construct a full name for this enum value Dim fullName As String = enumType.FullName +"." + name ' See if we have looked it up earlier Dim enumDescription As String = Nothing If _Descriptions.TryGetValue(fullName, enumDescription) Then ' Yes we have - return previous value Return enumDescription End If ' Find the value of the Description attribute on this enum value Dim members As MemberInfo() = enumType.GetMember(name) If members IsNot Nothing AndAlso members.Length > 0 Then Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False) If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then ' Set name to description found name = DirectCast(descriptions(0), DescriptionAttribute).Description End If End If ' Save the name in the dictionary: _Descriptions.Add(fullName, name) ' Return the name Return name End Function End Module |
由于使用
(对vb.net解决方案感到抱歉-将其转换为c应该比较困难,而且我的c对扩展之类的新主题很生疏)
这是对RayBooysen代码的一个更新,该代码使用通用的getCustomAttributes方法和linq使事情变得更加整洁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /// <summary> /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums. /// </summary> /// <typeparam name="T">The type of the struct.</typeparam> /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param> /// <returns>If the struct has a Description attribute, this method returns the description. Otherwise it just calls ToString() on the struct.</returns> /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks> public static string GetDescription<T>(this T enumerationValue) where T : struct { return enumerationValue.GetType().GetMember(enumerationValue.ToString()) .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false), (mi, ca) => ca.Description) .FirstOrDefault() ?? enumerationValue.ToString(); } |
更清晰的总结:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | using System; using System.Reflection; public class TextAttribute : Attribute { public string Text; public TextAttribute(string text) { Text = text; } } public static class EnumExtender { public static string ToText(this Enum enumeration) { var memberInfo = enumeration.GetType().GetMember(enumeration.ToString()); if (memberInfo.Length <= 0) return enumeration.ToString(); var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false); return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString(); } } |
与下划线描述的用法相同。
对于标记枚举,包括。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public static string Description(this Enum value) { Type type = value.GetType(); List<string> res = new List<string>(); var arrValue = value.ToString().Split(',').Select(v=>v.Trim()); foreach (string strValue in arrValue) { MemberInfo[] memberInfo = type.GetMember(strValue); if (memberInfo != null && memberInfo.Length > 0) { object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null) { res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description); } else res.Add(strValue); } else res.Add(strValue); } return res.Aggregate((s,v)=>s+","+v); } |
我认为解决问题的最好(也是最简单)方法是为枚举编写一个扩展方法:
1 2 3 4 | public static string GetUserFriendlyString(this PublishStatusses status) { } |
如果您想要完全可定制的东西,请在这里尝试我的解决方案:
http://www.kevinwilliampang.com/post/mapping-enums-to-strings-and-strings-to-enums-in-net.aspx
基本上,本文概述了如何将描述属性附加到每个枚举,并提供了一种从枚举映射到描述的通用方法。