¿Pueden mis enums tener nombres amistosos?

Tengo la siguiente enum

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

¿No es posible tener enum s con “nombres amistosos”?

Los nombres de valores Enum deben seguir las mismas reglas de denominación que todos los identificadores en C #, por lo tanto, solo el primer nombre es correcto.

Puede usar el atributo Description , como sugirió Yuriy. El siguiente método de extensión facilita la obtención de la descripción para un valor dado de la enumeración:

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

Puedes usarlo así:

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

Si tiene la siguiente enumeración:

 public enum MyEnum { First, Second, Third } 

Puede declarar métodos de extensión para MyEnum (como puede MyEnum para cualquier otro tipo). Acabo de azotar esto:

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

Con este Método de extensión, ahora es legal lo siguiente:

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

¡¡Espero que esto ayude!!

No, pero puede usar la DescripciónAtributo para lograr lo que está buscando.

Puede usar Descripción de atributo para obtener ese nombre descriptivo. Puedes usar el código a continuación:

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

Un problema con este truco es que el atributo de descripción no puede ser localizado. Me gusta una técnica de Sacha Barber, donde crea su propia versión de atributo Descripción que recogerá los valores del administrador de recursos correspondiente.

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

Aunque el artículo trata sobre un problema que generalmente enfrentan los desarrolladores de WPF cuando se vinculan a enumeraciones, puede saltar directamente a la parte donde crea LocalizableDescriptionAttribute.

Algunas grandes soluciones ya han sido publicadas. Cuando encontré este problema, quería ir en ambas direcciones: convertir una enumeración en una descripción y convertir una cadena que coincida con una descripción en una enumeración.

Tengo dos variantes, lento y rápido. Ambos convierten de enum a string y string a enum. Mi problema es que tengo enumeraciones como esta, donde algunos elementos necesitan atributos y otros no. No quiero poner atributos en elementos que no los necesitan. Tengo alrededor de un centenar de estos totales actualmente:

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

El primer método para lidiar con esto es lento, y se basa en las sugerencias que vi aquí y alrededor de la red. Es lento porque estamos reflexionando para cada conversión:

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

El problema con esto es que estás haciendo reflexiones todo el tiempo. No he medido el rendimiento alcanzado al hacerlo, pero parece alarmante. Peor aún, estamos calculando estas costosas conversiones repetidamente, sin almacenarlos en caché.

En su lugar, podemos usar un constructor estático para completar algunos diccionarios con esta información de conversión, luego simplemente busque esta información cuando sea necesario. Aparentemente las clases estáticas (requeridas para los métodos de extensión) pueden tener constructores y 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; } } } 

Mira qué tan ajustados son los métodos de conversión ahora. El único defecto que puedo pensar es que esto requiere que todas las enumeraciones convertidas estén en el ensamblaje actual. Además, solo me molesto con las enumeraciones exportadas, pero podrías cambiar eso si lo deseas.

Esta es la forma de llamar a los métodos

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

Después de leer muchos recursos sobre este tema, incluido StackOverFlow, descubro que no todas las soluciones funcionan correctamente. Debajo está nuestro bash de arreglar esto.

Básicamente, tomamos el nombre descriptivo de un Enum de un atributo Descripción si existe.
Si no es así, utilizamos RegEx para determinar las palabras dentro del nombre Enum y agregar espacios.

La próxima versión, utilizaremos otro atributo para marcar si podemos / debemos tomar el nombre descriptivo de un archivo de recursos localizable.

A continuación se encuentran los casos de prueba. Informe si tiene otro caso de prueba que no se aprueba.

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

Siguen las mismas reglas de denominación que los nombres de variable. Por lo tanto, no deberían contener espacios.

Además, lo que estás sugiriendo sería una mala práctica de todos modos.

Los nombres de Enum viven bajo las mismas reglas que los nombres de variable normales, es decir, sin espacios ni puntos en el medio de los nombres … Todavía considero que el primero es bastante amigable …

Esta es una idea terrible, pero 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. Sus nombres enum deben tener la misma cantidad de caracteres o más que la cadena que desea que sea.

  2. Tus nombres enum no deberían repetirse en ningún lado, solo en caso de que el interrogatorio de cuerdas estropee las cosas

Por qué esta es una mala idea (algunas razones)

  1. Tus nombres enum se vuelven feos debido a los requisitos

  2. Se basa en llamar al método de inicialización lo suficientemente temprano

  3. Punteros inseguros

  4. Si el formato interno de la cadena cambia, por ejemplo, si se mueve el campo de longitud, estás atornillado

  5. Si Enum.ToString () se cambia alguna vez para que devuelva solo una copia, está jodido

  6. Raymond Chen se quejará sobre su uso de características no documentadas, y cómo es su culpa que el equipo de CLR no haya podido hacer una optimización para reducir el tiempo de ejecución en un 50%, durante su próxima semana .NET.

Supongo que quiere mostrar sus valores enum al usuario, por lo tanto, quiere que tengan algún nombre descriptivo. Aquí está mi sugerencia: Use un patrón de tipo enum. Aunque debe hacer un esfuerzo para implementarlo, pero realmente lo vale.

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