Meus enums podem ter nomes amigáveis?

Eu tenho o seguinte enum

 public enum myEnum { ThisNameWorks, This Name doesn't work Neither.does.this; } 

Não é possível ter enum s com “nomes amigáveis”?

Os nomes de valores Enum devem seguir as mesmas regras de nomenclatura de todos os identificadores em C #, portanto, somente o primeiro nome está correto.

Você poderia usar o atributo Description , como sugerido por Yuriy. O método de extensão a seguir facilita a obtenção da descrição de um determinado valor do enum:

  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; } 

Você pode usá-lo assim:

 public enum MyEnum { [Description("Description for Foo")] Foo, [Description("Description for Bar")] Bar } MyEnum x = MyEnum.Foo; string description = x.GetDescription(); 

Se você tiver o seguinte enum:

 public enum MyEnum { First, Second, Third } 

Você pode declarar methods de extensão para MyEnum (como você pode para qualquer outro tipo). Eu apenas agitei isso:

 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!!"; } } } 

Com este método de extensão, o seguinte é agora legal:

 Console.WriteLine(MyEnum.First.EnumValue()); 

Espero que isto ajude!!

Não, mas você pode usar o DescriptionAttribute para realizar o que está procurando.

Você pode usar o Atributo de Descrição para obter esse nome amigável. Você pode usar o código abaixo:

 ///  /// Very good method to Override ToString on Enums /// Case : Suppose your enum value is EncryptionProviderType and you want /// enumVar.Tostring() to retrun "Encryption Provider Type" then you should use this method. /// Prerequisite : All enum members should be applied with attribute [Description("String to be returned by Tostring()")] /// Example : /// enum ExampleEnum /// { /// [Description("One is one")] /// ValueOne = 1, /// [Description("Two is two")] /// ValueTow = 2 /// } /// /// in your class /// ExampleEnum enumVar = ExampleEnum.ValueOne ; /// Console.WriteLine(ToStringEnums(enumVar)); ///  ///  ///  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(); } 

Um problema com esse truque é que o atributo de descrição não pode ser localizado. Eu gosto de uma técnica de Sacha Barber, onde ele cria sua própria versão do atributo Description, que selecionaria os valores do gerenciador de resources correspondente.

http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx

Embora o artigo esteja relacionado a um problema que geralmente é enfrentado pelos desenvolvedores do WPF ao vincular a enums, você pode ir diretamente para a parte em que ele cria o LocalizableDescriptionAttribute.

Algumas ótimas soluções já foram postadas. Quando eu encontrei esse problema, eu queria ir nos dois sentidos: converter um enum em uma descrição e converter uma seqüência de caracteres correspondente a uma descrição em um enum.

Eu tenho duas variantes, lentas e rápidas. Ambos convertem de enum para string e string para enum. Meu problema é que eu tenho enums como este, onde alguns elementos precisam de atributos e outros não. Eu não quero colocar atributos em elementos que não precisam deles. Eu tenho cerca de cem desses totais atualmente:

 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, }; 

O primeiro método para lidar com isso é lento, e é baseado em sugestões que vi aqui e ao redor da rede. É lento porque estamos refletindo para cada conversão:

 using System; using System.Collections.Generic; namespace CustomExtensions { ///  /// uses extension methods to convert enums with hypens in their names to underscore and other variants public static class EnumExtensions { ///  /// 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 ///  ///  ///  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; } ///  /// Converts a string to an enum field using the string first; if that fails, tries to find a description /// attribute that matches. /// "$".ToEnum() yields POS.Dollar ///  ///  ///  ///  public static T ToEnumSlow(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; } } } 

O problema com isso é que você está fazendo reflexões todas as vezes. Eu não medi o impacto no desempenho ao fazê-lo, mas parece alarmante. Pior, estamos computando essas conversões caras repetidamente, sem armazená-las em cache.

Em vez disso, podemos usar um construtor estático para preencher alguns dictionarys com essas informações de conversão. Depois, basta procurar essas informações quando necessário. Aparentemente, classs estáticas (necessárias para methods de extensão) podem ter construtores e campos 🙂

 using System; using System.Collections.Generic; namespace CustomExtensions { ///  /// 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. ///  public static class EnumExtensions { //To avoid collisions, every Enum type has its own hash table private static readonly Dictionary> enumToStringDictionary = new Dictionary>(); private static readonly Dictionary> stringToEnumDictionary = new Dictionary>(); static EnumExtensions() { //let's collect the enums we care about List enumTypeList = new List(); //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() ); EnumExtensions.stringToEnumDictionary.Add(type, new Dictionary() ); 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(this string value) { Type type = typeof(T); T theEnum = (T)EnumExtensions.stringToEnumDictionary[type][value]; return theEnum; } } } 

Veja como os methods de conversão são curtos agora. A única falha que posso imaginar é que isso requer que todas as enums convertidas estejam na assembly atual. Além disso, eu só me preocupo com enums exportados, mas você pode mudar isso se desejar.

Isto é como chamar os methods

  string x = LthWrapper.POS.Dollar.GetString(); LthWrapper.POS y = "PRP$".ToEnum(); 
 public enum myEnum { ThisNameWorks, This_Name_can_be_used_instead, } 

Depois de ler muitos resources sobre esse tópico, incluindo o StackOverFlow, descubro que nem todas as soluções estão funcionando corretamente. Abaixo está nossa tentativa de consertar isso.

Basicamente, tomamos o nome amigável de um Enum de um DescriptionAttribute, se existir.
Se isso não acontecer, usamos o RegEx para determinar as palavras dentro do nome do Enum e adicionar espaços.

Na próxima versão, usaremos outro Attribute para sinalizar se podemos / devemos usar o nome amigável de um arquivo de recurso localizável.

Abaixo estão os casos de teste. Por favor, informe se você tiver outro caso de teste que não passe.

 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 = @"([AZ]+(?![az])|\d+|[AZ][az]+|(?![AZ])[az]+)+"; List words = new List(); 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); } } } 

Eles seguem as mesmas regras de nomeação que nomes de variables. Portanto, eles não devem conter espaços.

Também o que você está sugerindo seria uma prática muito ruim de qualquer maneira.

Os nomes Enum vivem sob as mesmas regras dos nomes das variables ​​normais, ou seja, sem espaços ou pontos no meio dos nomes … Eu ainda considero o primeiro como bastante amigável …

Esta é uma ideia terrível, mas funciona.

  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); } 

Requisitos

  1. Seus nomes de enum devem ter o mesmo número de caracteres ou mais que a string que você deseja que ele seja.

  2. Seus nomes de enum não devem ser repetidos em nenhum lugar, apenas para o caso de a internação de strings atrapalhar as coisas

Por que isso é uma má ideia (algumas razões)

  1. Seus nomes de enum tornam-se feios devido aos requisitos

  2. Ele depende de você chamar o método de boot com antecedência suficiente

  3. Ponteiros inseguros

  4. Se o formato interno da string mudar, por exemplo, se o campo length for movido, você está ferrado

  5. Se Enum.ToString () é sempre alterado para que ele retorne apenas uma cópia, você está ferrado

  6. Raymond Chen vai reclamar do seu uso de resources não documentados, e como é sua culpa que a equipe do CLR não possa fazer uma otimização para reduzir o tempo de execução em 50%, durante sua próxima semana do .NET.

Eu suponho que você quer mostrar seus valores enum para o usuário, portanto, você quer que eles tenham algum nome amigável. Aqui está minha sugestão: Use um padrão de tipo enum. Embora você deva fazer algum esforço para implementá-lo, mas realmente vale a pena.

 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; } }