用户友好的字符串的枚举ToString
我的枚举包含以下值:
private enum PublishStatusses{ NotCompleted, Completed, Error };
我希望能够以用户友好的方式输出这些值。
我不需要再能够从字符串去值。
我使用System.ComponentModel命名空间中的Description
属性。 简单地装饰枚举:
private enum PublishStatusValue { [Description("Not Completed")] NotCompleted, Completed, Error };
然后使用这个代码来检索它:
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(); }
我用扩展方法做到这一点:
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!"; } } }
也许我错过了什么,但是Enum.GetName有什么问题?
public string GetName(PublishStatusses value) { return Enum.GetName(typeof(PublishStatusses), value) }
编辑:对于用户友好的字符串,您需要通过一个.resource来完成国际化/本地化,并且使用基于枚举键的固定键比装饰器属性更好。
最简单的解决方案是使用自定义的扩展方法(至少在.NET 3.5中 – 您可以将其转换为早期框架版本的静态辅助方法)。
public static string ToCustomString(this PublishStatusses value) { switch(value) { // Return string depending on value. } return null; }
我在这里假设你想要返回除了枚举值的实际名称以外的东西(可以通过简单地调用ToString来获得)。
我创建了一个反向扩展方法将描述转换回枚举值:
public static T ToEnumValue<T>(this string enumerationDescription) where T : struct { Type 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"); }
另一篇文章是Java。 你不能把方法放在C#中的枚举中。
只是做这样的事情:
PublishStatusses status = ... String s = status.ToString();
如果你想为你的枚举值使用不同的显示值,你可以使用属性和反射。
关于Ray Booysen,代码中有一个错误: 使用用户友好字符串的Enum ToString
您需要考虑枚举值的多个属性。
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();
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; }
避免类/引用类型的一些其他更原始的选项:
- 数组方法
- 嵌套的结构方法
数组方法
private struct PublishStatusses { public static string[] Desc = { "Not Completed", "Completed", "Error" }; public enum Id { NotCompleted = 0, Completed, Error }; }
用法
string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];
嵌套的结构方法
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"; } }
用法
int id = PublishStatusses.NotCompleted.Id; string desc = PublishStatusses.NotCompleted.Desc;
上述建议与样本的清洁总结:
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
用法:
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
最简单的方法就是将这个扩展类包含到您的项目中,它将与项目中的任何枚举一起工作:
public static class EnumExtensions { public static string ToFriendlyString(this Enum code) { return Enum.GetName(code.GetType(), code); } }
用法:
enum ExampleEnum { Demo = 0, Test = 1, Live = 2 }
…
ExampleEnum ee = ExampleEnum.Live; Console.WriteLine(ee.ToFriendlyString());
你可以使用人性 化的可能性使用Humanizer包。 一个例子:
enum PublishStatusses { [Description("Custom description")] NotCompleted, AlmostCompleted, Error };
那么你可以直接在枚举上使用Humanize
扩展方法:
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)
这是对Ray Booysen代码的更新,它使用通用的GetCustomAttributes方法和LINQ来使事情变得更加整洁。
/// <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(); }
我碰巧是一个VB.NET粉丝,所以这是我的版本,将DescriptionAttribute方法与扩展方法结合在一起。 一,结果:
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。 “魔术”发生在Sub Main()中的Console.WriteLine调用中,最后一个参数是v.Description
。 V1返回“1”,V2返回“V2”,V3返回“三”。 这个Description-method实际上是一个扩展方法,在另一个名为EnumExtensions的模块中定义:
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
由于使用Reflection
来查找描述属性的速度很慢,因此查找也会缓存在一个私有Dictionary
,该Dictionary
按需填充。
(抱歉对VB.NET解决方案 – 它应该是相对直接转换为C#,我的C#是生锈的新主题,如扩展)
即使更简洁:
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(); } }
与下划线描述的用法相同。
包括标志枚举。
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); }
我认为解决您的问题的最好(也是最简单的)方法是为您的枚举编写一个扩展方法:
public static string GetUserFriendlyString(this PublishStatusses status) { }
如果你想要完全自定义的东西,请在这里试试我的解决方案:
http://www.kevinwilliampang.com/post/Mapping-Enums-To-Strings-and-Strings-to-Enums-in-NET.aspx
基本上,这篇文章概述了如何将Description属性附加到每个枚举,并提供了一个通用的方法来从枚举映射到描述。