español enumeraciones enum c# .net

español - Asociación de enumeraciones con cadenas en C#



enum class c# (30)

Sé que lo siguiente no es posible porque tiene que ser un int.

enum GroupTypes { TheGroup = "OEM", TheOtherGroup = "CMB" }

De mi base de datos obtengo un campo con códigos incomprensivos (el OEM y el CMB). Me gustaría convertir este campo en una enumeración o algo más comprensible. Debido a que el objetivo es la legibilidad, la solución debe ser concisa.
¿Qué otras opciones tengo?


¿Has considerado una tabla de búsqueda utilizando un Diccionario?

enum GroupTypes { TheGroup, TheOtherGroup } Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>(); // initialize lookup table: GroupTypeLookup.Add("OEM", TheGroup); GroupTypeLookup.Add("CMB", TheOtherGroup);

Luego puede usar GroupTypeLookup.TryGetValue () para buscar una cadena cuando la lea.


¿Por qué no usar ToString () en los tipos Enum?

private enum LogType { Error, Warning, Info}; private WriteLog(string message, LogType logType) { Log.Write(message, logType.ToString()); }


¿Qué hay de usar una clase estática con constantes? El código del cliente no será diferente de enums.

static class GroupTypes { public const string TheGroup = "OEM"; public const string TheOtherGroup = "CMB"; } void DoSomething(GroupTypes groupType) { if(groupType == GroupTypes.TheOtherGroup) { //Launch nuclear bomb } }


Aquí está el método de extensión que utilicé para obtener el valor de enumeración como cadena. Primero aquí está la enumeración.

public enum DatabaseEnvironment { [Description("AzamSharpBlogDevDatabase")] Development = 1, [Description("AzamSharpBlogQADatabase")] QualityAssurance = 2, [Description("AzamSharpBlogTestDatabase")] Test = 3 }

El atributo Descripción vino de System.ComponentModel.

Y aquí está mi método de extensión:

public static string GetValueAsString(this DatabaseEnvironment environment) { // get the field var field = environment.GetType().GetField(environment.ToString()); var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false); if(customAttributes.Length > 0) { return (customAttributes[0] as DescriptionAttribute).Description; } else { return environment.ToString(); } }

Ahora, puede acceder a la enumeración como valor de cadena utilizando el siguiente código:

[TestFixture] public class when_getting_value_of_enum { [Test] public void should_get_the_value_as_string() { Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString()); } }


Básicamente estaba buscando la respuesta de Reflexión de @ArthurC

Solo para ampliar un poco su respuesta, puede mejorarla aún más teniendo una función genérica:

// If you want for a specific Enum private static string EnumStringValue(GroupTypes e) { return EnumStringValue<GroupTypes>(e); } // Generic private static string EnumStringValue<T>(T enumInstance) { return Enum.GetName(typeof(T), enumInstance); }

Entonces puedes simplemente envolver lo que tengas

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

o

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic


Basado en otras opiniones, esto es lo que se me ocurre. Este enfoque evita tener que escribir .Value donde desea obtener el valor constante.

Tengo una clase base para todas las enumeraciones de cadena como esta:

using System; using Newtonsoft.Json; [JsonConverter(typeof(ConstantConverter))] public class StringEnum: IConvertible { public string Value { get; set; } protected StringEnum(string value) { Value = value; } public static implicit operator string(StringEnum c) { return c.Value; } public string ToString(IFormatProvider provider) { return Value; } public TypeCode GetTypeCode() { throw new NotImplementedException(); } public bool ToBoolean(IFormatProvider provider) { throw new NotImplementedException(); } //The same for all the rest of IConvertible methods }

El JsonConverter es así:

using System; using Newtonsoft.Json; class ConstantConverter : JsonConverter { public override bool CanConvert(Type objectType) { return true; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { throw new NotImplementedException(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { if (value == null) { serializer.Serialize(writer, null); } else { serializer.Serialize(writer, value.ToString()); } } }

Y un enum de cadena real será algo como esto:

public sealed class Colors : StringEnum { public static Colors Red { get { return new Catalog("Red"); } } public static Colors Yellow { get { return new Catalog("Yellow"); } } public static Colors White { get { return new Catalog("White"); } } private Colors(string value) : base(value) { } }

Y con esto, puedes usar Color.Red para incluso serializar a json sin usar la propiedad Value


C # no admite cadenas enumeradas, pero para la mayoría de las situaciones, puede usar una Lista o Diccionario para obtener el efecto deseado.

Por ejemplo, para imprimir los resultados de pasar / fallar:

List<string> PassFail = new List<string> { "FAIL", "PASS" }; bool result = true; Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);


Cree una segunda enumeración para su base de datos que contenga lo siguiente:

enum DBGroupTypes { OEM = 0, CMB = 1 }

Ahora, puede usar Enum.Parse para recuperar el valor DBGroupTypes correcto de las cadenas "OEM" y "CMB". Luego, puede convertirlos a int y recuperar los valores correctos de la enumeración correcta que desea usar en su modelo.


En VS 2015, puedes usar nameof

public class LogCategory { public static string Trace; public static string Debug; public static string Info; public static string Warning; public static string Error; }

Uso:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));


Esta es una forma de usarlo como un parámetro fuertemente tipado o como una cadena :

public class ClassLikeEnum { public string Value { get; private set; } ClassLikeEnum(string value) { Value = value; } public static implicit operator string(ClassLikeEnum c) { return c.Value; } public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1"); public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2"); }


He hecho algo como esto;

public enum BusinessUnits { NEW_EQUIPMENT = 0, USED_EQUIPMENT = 1, RENTAL_EQUIPMENT = 2, PARTS = 3, SERVICE = 4, OPERATOR_TRAINING = 5 } public class BusinessUnitService { public static string StringBusinessUnits(BusinessUnits BU) { switch (BU) { case BusinessUnits.NEW_EQUIPMENT: return "NEW EQUIPMENT"; case BusinessUnits.USED_EQUIPMENT: return "USED EQUIPMENT"; case BusinessUnits.RENTAL_EQUIPMENT: return "RENTAL EQUIPMENT"; case BusinessUnits.PARTS: return "PARTS"; case BusinessUnits.SERVICE: return "SERVICE"; case BusinessUnits.OPERATOR_TRAINING: return "OPERATOR TRAINING"; default: return String.Empty; } } }

Llámalo con esto;

BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS)


Incluso implementé algunas enumeraciones según lo sugerido por @Even (a través de la class X y miembros public static X ), solo para descubrir más adelante que en estos días, a partir de .Net 4.5, existe el método ToString() correcto .

Ahora estoy reimplementando todo de nuevo a enumeraciones.


Intenta agregar constantes a una clase estática. No terminas con un Tipo, pero tienes constantes legibles y organizadas:

public static class GroupTypes { public const string TheGroup = "OEM"; public const string TheOtherGroup = "CMB" }


Me gusta usar propiedades en una clase en lugar de métodos, ya que se parecen más a una enumeración.

Aquí hay un ejemplo para un registrador:

public class LogCategory { private LogCategory(string value) { Value = value; } public string Value { get; set; } public static LogCategory Trace { get { return new LogCategory("Trace"); } } public static LogCategory Debug { get { return new LogCategory("Debug"); } } public static LogCategory Info { get { return new LogCategory("Info"); } } public static LogCategory Warning { get { return new LogCategory("Warning"); } } public static LogCategory Error { get { return new LogCategory("Error"); } } }

Pase los valores de cadena de tipo seguro como un parámetro:

public static void Write(string message, LogCategory logCategory) { var log = new LogEntry { Message = message }; Logger.Write(log, logCategory.Value); }

Uso:

Logger.Write("This is almost like an enum.", LogCategory.Info);


Me gustaría hacer en una clase y evitar una enumeración por completo. Y luego, con el uso de un manejador de tipos, puede crear el objeto cuando lo toma de la base de datos.

ES DECIR:

public class Group { public string Value{ get; set; } public Group( string value ){ Value = value; } public static Group TheGroup() { return new Group("OEM"); } public static Group OtherGroup() { return new Group("CMB"); } }


Mi primera pregunta: ¿Tiene acceso a la base de datos? Esto debería normalizarse en la base de datos, idealmente, de lo contrario, cualquier solución será propensa a errores. En mi experiencia, los campos de datos llenos de "OEM" y "CMB" tienden a terminar teniendo cosas como "OEM" y otros ''datos de basura'' mezclados a lo largo del tiempo ... Si puede normalizarlo, puede usar la tecla en la tabla que contiene los elementos como tu Enum, y listo, con una estructura mucho más limpia.

Si eso no está disponible, haría tu Enum, y haría una clase para analizar tu cadena en el Enum para ti. Al menos, esto le daría cierta flexibilidad en el manejo de entradas no estándar y mucha más flexibilidad para atrapar o manejar errores que hacer cualquiera de las soluciones alternativas utilizando Enum.Parse / Reflection / etc. Un diccionario funcionaría, pero podría descomponerse si alguna vez tiene problemas de casos, etc.

Recomiendo escribir una clase para que puedas hacer:

// I renamed this to GroupType, since it sounds like each element has a single type... GroupType theType = GroupTypeParser.GetGroupType(theDBString);

Esto preserva la mayor parte de su legibilidad sin tener que cambiar el DB.


No necesitaba nada robusto como almacenar la cadena en atributos. Solo necesitaba convertir algo como MyEnum.BillEveryWeek en "factura cada semana" o MyEnum.UseLegacySystem en "usar sistema heredado", básicamente dividir la enumeración por su carcasa de camello en palabras individuales en minúsculas.

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false) { var characters = input.ToString().Select((x, i) => { if (i > 0 && char.IsUpper(x)) { return delimiter + x.ToString(CultureInfo.InvariantCulture); } return x.ToString(CultureInfo.InvariantCulture); }); var result = preserveCasing ? string.Concat(characters) : string.Concat(characters).ToLower(); var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal); if (lastComma > -1) { result = result.Remove(lastComma, 2).Insert(lastComma, " and "); } return result; }

MyEnum.UseLegacySystem.UnCamelCase() salida a "usar el sistema heredado"

Si tiene varias banderas establecidas, las convertirá en inglés simple (delimitado por comas, excepto "y" en lugar de la última coma).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes; Console.WriteLine(myCustomerBehaviour.UnCamelCase()); //outputs "bill every week, use legacy system and charge taxes"


Otra forma de lidiar con el problema, es tener una enumeración y una matriz de cadenas que asignen los valores de enumeración con la lista de cadenas:

public enum GroupTypes { TheGroup = 0, TheOtherGroup } string[] GroupTypesStr = { "OEM", "CMB" };

Puedes usarlo así:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

Se le pedirá a CMB

PROS:

  1. Código fácil y limpio.
  2. Alto rendimiento (especialmente en comparación con aquellos enfoques que utilizan clases)

CONTRAS:

  1. Es propenso a desordenar la lista al editarla, pero estará bien para una lista corta.

Puede agregar atributos a los elementos en la enumeración y luego usar la reflexión para obtener los valores de los atributos.

Tendría que usar el especificador de "campo" para aplicar los atributos, así:

enum GroupTypes { [field:Description("OEM")] TheGroup, [field:Description("CMB")] TheOtherGroup }

Luego, debería reflexionar sobre los campos estáticos del tipo de enumeración (en este caso, GroupTypes) y obtener el atributo de DescriptionAttribute para el valor que estaba buscando mediante la reflexión:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>( this T value) where T : struct { // The type of the enum, it will be reused. Type type = typeof(T); // If T is not an enum, get out. if (!type.IsEnum) throw new InvalidOperationException( "The type parameter T must be an enum type."); // If the value isn''t defined throw an exception. if (!Enum.IsDefined(type, value)) throw new InvalidEnumArgumentException( "value", Convert.ToInt32(value), type); // Get the static field for the value. FieldInfo fi = type.GetField(value.ToString(), BindingFlags.Static | BindingFlags.Public); // Get the description attribute, if there is one. return fi.GetCustomAttributes(typeof(DescriptionAttribute), true). Cast<DescriptionAttribute>().SingleOrDefault(); }

Opté por devolver el Atributo de DescriptionAttribute anterior, en el caso de que desee poder determinar si el atributo se aplica o no.


Puede utilizar dos enumeraciones. Uno para la base de datos y otro para la legibilidad.

Solo debes asegurarte de que estén sincronizados, lo que parece un pequeño costo. No tiene que establecer los valores, solo establezca las posiciones iguales, pero al establecer los valores queda muy claro que las dos enumeraciones están relacionadas y evita que los errores reorganicen los miembros de la enumeración. Y un comentario le permite al equipo de mantenimiento saber que están relacionados y que deben mantenerse sincronizados.

// keep in sync with GroupTypes public enum GroupTypeCodes { OEM, CMB } // keep in sync with GroupTypesCodes public enum GroupTypes { TheGroup = GroupTypeCodes.OEM, TheOtherGroup = GroupTypeCodes.CMB }

Para usarlo solo conviertes al código primero:

GroupTypes myGroupType = GroupTypes.TheGroup; string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

Entonces, si desea que sea aún más conveniente, puede agregar una función de extensión que solo funcione para este tipo de enumeración:

public static string ToString(this GroupTypes source) { return ((GroupTypeCodes)source).ToString(); }

y entonces puedes simplemente hacer:

GroupTypes myGroupType = GroupTypes.TheGroup; string valueToSaveIntoDatabase = myGroupType.ToString();


Puedes hacerlo muy fácilmente en realidad. Usa el siguiente código.

enum GroupTypes { OEM, CMB };

Luego, cuando desee obtener el valor de cadena de cada elemento enum, simplemente use la siguiente línea de código.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

He usado este método con éxito en el pasado, y también he usado una clase de constantes para mantener constantes de cadenas, ambas funcionan bastante bien, pero tiendo a preferir esto.


Respuesta de Even:

public class LogCategory { private LogCategory(string value) { Value = value; } public string Value { get; set; } public static LogCategory Trace { get { return new LogCategory("Trace"); } } public static LogCategory Debug { get { return new LogCategory("Debug"); } } public static LogCategory Info { get { return new LogCategory("Info"); } } public static LogCategory Warning { get { return new LogCategory("Warning"); } } public static LogCategory Error { get { return new LogCategory("Error"); } } }

Solo quería agregar una forma de simular el cambio con enumeraciones basadas en clase:

public void Foo(LogCategory logCategory){ var @switch = new Dictionary<LogCategory, Action>{ {LogCategory.Trace, ()=>Console.Writeline("Trace selected!")}, {LogCategory.Debug, ()=>Console.Writeline("Debug selected!")}, {LogCategory.Error, ()=>Console.Writeline("Error selected!")}}; //will print one of the line based on passed argument @switch[logCategory](); }


Si entiendo correctamente, necesita una conversión de cadena a enumeración:

enum GroupTypes { Unknown = 0, OEM = 1, CMB = 2 } static GroupTypes StrToEnum(string str){ GroupTypes g = GroupTypes.Unknown; try { object o = Enum.Parse(typeof(GroupTypes), str, true); g = (GroupTypes)(o ?? 0); } catch { } return g; } // then use it like this GroupTypes g1 = StrToEnum("OEM"); GroupTypes g2 = StrToEnum("bad value");

Puede hacerlo más elegante con genéricos para el tipo de enumeración si lo desea.


Si estás tratando de hacer que tu código sea legible:

class GroupTypes { public static final String (whatever oem stands for) = "OEM"; public static final String (whatever cmb stands for) = "CMB"; ... }

y si necesita una lista de ellos, incluya estas finales en una lista final estática <String>. Este ejemplo está en Java.

Si está intentando hacer que su aplicación sea legible, agregue:

public static final Map<String, String> groupsByDbValue; static { groupsByDbValue = new HashMap<String, String>(); groupsByDbValue.put("OEM", "(whatever OEM stands for)"); groupsByDbValue.put("CMB", "(whatever CMB stands for)"); }


Simplemente crearía un diccionario y usaría el código como la clave.

Edición: Para abordar el comentario sobre hacer una búsqueda inversa (encontrar la clave), esto no sería terriblemente eficiente. Si esto es necesario, escribiría una nueva clase para manejarlo.


También puedes usar el modelo de extensión:

public enum MyEnum { [Description("String 1")] V1= 1, [Description("String 2")] V2= 2 }

Tu clase de extensión

public static class MyEnumExtensions { public static string ToDescriptionString(this MyEnum val) { DescriptionAttribute[] attributes = (DescriptionAttribute[])val .GetType() .GetField(val.ToString()) .GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes.Length > 0 ? attributes[0].Description : string.Empty; } }

uso:

MyEnum myLocal = MyEnum.V1; print(myLocal.ToDescriptionString());


Un pequeño ajuste al método de Extensión Glennular, por lo que podría usar la extensión en otras cosas que no sean solo ENUM;

using System; using System.ComponentModel; namespace Extensions { public static class T_Extensions { /// <summary> /// Gets the Description Attribute Value /// </summary> /// <typeparam name="T">Entity Type</typeparam> /// <param name="val">Variable</param> /// <returns>The value of the Description Attribute or an Empty String</returns> public static string Description<T>(this T t) { DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes.Length > 0 ? attributes[0].Description : string.Empty; } } }

O usando linq

using System; using System.ComponentModel; using System.Linq; namespace Extensions { public static class T_Extensions { public static string Description<T>(this T t) => ((DescriptionAttribute[])t ?.GetType() ?.GetField(t?.ToString()) ?.GetCustomAttributes(typeof(DescriptionAttribute), false)) ?.Select(a => a?.Description) ?.FirstOrDefault() ?? string.Empty; } }


Usa una clase.

Editar: mejor ejemplo

class StarshipType { private string _Name; private static List<StarshipType> _StarshipTypes = new List<StarshipType>(); public static readonly StarshipType Ultralight = new StarshipType("Ultralight"); public static readonly StarshipType Light = new StarshipType("Light"); public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight"); public static readonly StarshipType Heavy = new StarshipType("Heavy"); public static readonly StarshipType Superheavy = new StarshipType("Superheavy"); public string Name { get { return _Name; } private set { _Name = value; } } public static IList<StarshipType> StarshipTypes { get { return _StarshipTypes; } } private StarshipType(string name, int systemRatio) { Name = name; _StarshipTypes.Add(this); } public static StarshipType Parse(string toParse) { foreach (StarshipType s in StarshipTypes) { if (toParse == s.Name) return s; } throw new FormatException("Could not parse string."); } }


las enumeraciones en C # están restringidas a tipos numéricos enteros subyacentes (byte, sbyte, short, ushort, int, uint, long y ulong). No puede asociarlos con un valor subyacente basado en caracteres o cadenas.

Un enfoque diferente podría ser definir un diccionario de tipo Diccionario <cadena, cadena>.


public class DataType { private readonly string value; private static readonly Dictionary<string, DataType> predefinedValues; public static readonly DataType Json = new DataType("json"); public static readonly DataType Xml = new DataType("xml"); public static readonly DataType Text = new DataType("text"); public static readonly DataType Html = new DataType("html"); public static readonly DataType Binary = new DataType("binary"); static DataType() { predefinedValues = new Dictionary<string, DataType>(); predefinedValues.Add(Json.Value, Json); predefinedValues.Add(Xml.Value, Xml); predefinedValues.Add(Text.Value, Text); predefinedValues.Add(Html.Value, Html); predefinedValues.Add(Binary.Value, Binary); } private DataType(string value) { this.value = value; } public static DataType Parse(string value) { var exception = new FormatException($"Invalid value for type {nameof(DataType)}"); if (string.IsNullOrEmpty(value)) throw exception; string key = value.ToLower(); if (!predefinedValues.ContainsKey(key)) throw exception; return predefinedValues[key]; } public string Value { get { return value; } } }