我有以下enum
public enum myEnum
{
ThisNameWorks,
This Name doesn't work
Neither.does.this;
}
enum
是否无法使用“友好名称”?
答案 0 :(得分:359)
您可以使用Description
属性,如Yuriy建议的那样。以下扩展方法可以轻松获取枚举的给定值的描述:
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;
}
你可以像这样使用它:
public enum MyEnum
{
[Description("Description for Foo")]
Foo,
[Description("Description for Bar")]
Bar
}
MyEnum x = MyEnum.Foo;
string description = x.GetDescription();
答案 1 :(得分:74)
枚举值名称必须遵循与C#中所有标识符相同的命名规则,因此只有名字正确。
答案 2 :(得分:33)
如果您有以下枚举:
public enum MyEnum {
First,
Second,
Third
}
您可以为MyEnum
声明扩展方法(就像任何其他类型一样)。我只是掀起了这个:
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!!";
}
}
}
使用此扩展方法,以下内容现在是合法的:
Console.WriteLine(MyEnum.First.EnumValue());
希望这会有所帮助!!
答案 3 :(得分:23)
不,但您可以使用DescriptionAttribute来完成您正在寻找的目标。
答案 4 :(得分:12)
您可以使用Description
属性获取该友好名称。您可以使用以下代码:
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();
}
您希望何时使用此方法的示例:当您的枚举值为EncryptionProviderType
并且您希望enumVar.Tostring()
返回“加密提供程序类型”时。
先决条件:所有枚举成员都应使用属性[Description("String to be returned by Tostring()")]
。
枚举示例:
enum ExampleEnum
{
[Description("One is one")]
ValueOne = 1,
[Description("Two is two")]
ValueTow = 2
}
在你的课堂上,你会像这样使用它:
ExampleEnum enumVar = ExampleEnum.ValueOne;
Console.WriteLine(ToStringEnums(enumVar));
答案 5 :(得分:10)
这个技巧的一个问题是描述属性无法本地化。我喜欢Sacha Barber的一种技术,他在那里创建了自己的Description属性版本,该属性将从相应的资源管理器中获取值。
http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx
虽然本文是围绕WPF开发人员绑定到枚举时通常遇到的问题,但您可以直接跳转到他创建LocalizableDescriptionAttribute的部分。
答案 6 :(得分:7)
已经发布了一些很棒的解决方案。当我遇到这个问题时,我想双向进行:将枚举转换为描述,并将匹配描述的字符串转换为枚举。
我有两种变体,慢和快。两者都从枚举转换为字符串,字符串转换为枚举。我的问题是我有这样的枚举,其中一些元素需要属性,有些则不需要。我不想将属性放在不需要它们的元素上。我目前总共有大约一百个:
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,
};
处理此问题的第一种方法很慢,并且基于我在此处和网络上看到的建议。它很慢,因为我们反映每次转换:
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;
}
}
}
这个问题是你每次都在做反思。我没有测量过这样做的性能,但似乎令人担忧。更糟糕的是,我们反复计算这些昂贵的转换,而不是缓存它们。
相反,我们可以使用静态构造函数来填充一些带有此转换信息的字典,然后在需要时查找此信息。显然,静态类(扩展方法所需)可以有构造函数和字段:)
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;
}
}
}
看看转换方法现在有多紧张。我能想到的唯一缺陷是,这需要所有转换后的枚举都在当前程序集中。此外,我只打扰导出的枚举,但如果你愿意,你可以改变它。
这是如何调用方法
string x = LthWrapper.POS.Dollar.GetString();
LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();
答案 7 :(得分:4)
public enum myEnum
{
ThisNameWorks,
This_Name_can_be_used_instead,
}
答案 8 :(得分:4)
在阅读了有关此主题的许多资源(包括StackOverFlow)后,我发现并非所有解决方案都能正常工作。以下是我们尝试解决此问题。
基本上,我们从DescriptionAttribute中获取Enum的友好名称(如果存在) 如果不是,我们使用RegEx来确定枚举名称中的单词并添加空格。
下一个版本,我们将使用另一个属性来标记我们是否可以/应该从可本地化的资源文件中获取友好名称。
以下是测试用例。如果您有另一个未通过的测试用例,请报告。
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);
}
}
}
答案 9 :(得分:3)
它们遵循与变量名称相同的命名规则。 因此,它们不应包含空格。
无论如何,你所建议的也是非常糟糕的做法。
答案 10 :(得分:2)
枚举名称与普通变量名称的规则相同,即名称中间没有空格或点...我仍然认为第一个是相当友好的... ...
答案 11 :(得分:0)
这是一个糟糕的主意,但确实有效。
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);
}
要求
您的枚举名称必须与您希望的字符串具有相同的字符数或更多。
你的枚举名称不应该在任何地方重复,以防万一字符串实习混乱
为什么这是一个坏主意(有几个原因)
由于要求
它依赖于您尽早调用初始化方法
不安全指针
如果字符串的内部格式发生变化,例如如果长度字段被移动,那你就搞砸了
如果更改了Enum.ToString()以便它只返回一个副本,那么你就搞砸了
Raymond Chen会抱怨您使用未记录的功能,以及CLR团队在下一个.NET周期间无法优化将运行时间缩短50%的错误。
答案 12 :(得分:-2)
我想你想向用户展示你的枚举值,因此,你希望他们有一些友好的名字。
这是我的建议:
使用枚举类型模式。虽然需要付出一些努力才能实现,但这确实值得。
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;
}
}