Associando enums com strings em c #

Eu sei o seguinte não é possível porque tem que ser um int

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

Do meu database recebo um campo com códigos incompreensíveis (o OEM e o CMB). Eu gostaria de fazer este campo em um enum ou algo mais compreensível. Como o alvo é legível, a solução deve ser concisa.
Quais outras opções eu tenho?

Eu gosto de usar propriedades em uma class em vez de methods, pois eles parecem mais semelhantes a enum.

Aqui está um exemplo para um 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"); } } } 

Passe em valores de sequência de caracteres seguros como 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); 

Você também pode usar o modelo de extensão:

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

Sua class de extensão

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

Que tal usar uma class estática com constantes? O código do cliente não 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 } } 

Você pode adicionar atributos aos itens na enumeração e, em seguida, usar a reflection para obter os valores dos atributos.

Você teria que usar o especificador “field” para aplicar os atributos, da seguinte forma:

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

Você refletiria sobre os campos estáticos do tipo do enum (neste caso, GroupTypes) e obteria o DescriptionAttribute para o valor que procurava usando reflection:

 public static DescriptionAttribute GetEnumDescriptionAttribute( 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().SingleOrDefault(); } 

Optei por retornar o próprio DescriptionAttribute acima, no caso de você querer determinar se o atributo é ou não aplicado.

Você pode fazer isso muito facilmente, na verdade. Use o seguinte código.

 enum GroupTypes { OEM, CMB }; 

Então, quando você quiser obter o valor de string de cada elemento enum, use a seguinte linha de código.

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

Eu usei este método com sucesso no passado, e eu também usei uma class de constantes para manter constantes de string, ambos funcionam muito bem, mas eu tendem a preferir isso.

Crie um segundo enum, para o seu database contendo o seguinte:

 enum DBGroupTypes { OEM = 0, CMB = 1 } 

Agora, você pode usar Enum.Parse para recuperar o valor DBGroupTypes correto das seqüências de caracteres “OEM” e “CMB”. Você pode então convertê-los para int e recuperar os valores corretos da enumeração correta que deseja usar em seu modelo.

Use uma aula.

Editar: melhor exemplo

 class StarshipType { private string _Name; private static List _StarshipTypes = new List(); 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 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."); } } 

Tente adicionar constantes a uma class estática. Você não termina com um Type, mas tem constantes organizadas e legíveis:

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

Aqui está o método de extensão que usei para obter o valor enum como string. Primeiro aqui é o enum.

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

O atributo Description veio de System.ComponentModel.

E aqui está o meu método de extensão:

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

Agora, você pode acessar o enum como valor de string usando o seguinte 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()); } } 

Outra maneira de lidar com o problema é ter um enum e uma matriz de strings que mapeará os valores enum com a lista de strings:

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

você pode usar algo assim:

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

Ele irá solicitar CMB

PROS:

  1. Código fácil e limpo.
  2. Alto desempenho (especialmente em comparação com as abordagens que usam classs)

CONS:

  1. Propenso a bagunçar a lista ao editá-la, mas tudo ficará bem para uma pequena lista.

Você já considerou uma tabela de consulta usando um Dicionário?

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

Em seguida, você pode usar GroupTypeLookup.TryGetValue () para procurar uma seqüência de caracteres quando você lê.

Resposta da 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"); } } } 

Só queria adicionar uma maneira de imitar switch com enums de class:

 public void Foo(LogCategory logCategory){ var @switch = new Dictionary{ {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](); } 

C # não suporta strings enumeradas, mas para a maioria das situações você pode usar uma List ou Dictionary para obter o efeito desejado.

Por exemplo, para imprimir resultados de aprovação / reprovação:

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

Eu faria isso em uma aula e evitaria um enum completamente. E então, com o uso de um manipulador de tipos, você poderia criar o object quando o pegasse do database.

IE:

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

Gostaria apenas de criar um dictionary e usar o código como chave.

Edit: Para abordar o comentário sobre como fazer uma pesquisa inversa (encontrar a chave), isso não seria muito eficiente. Se isso for necessário, eu escreveria uma nova class para lidar com isso.

Minha primeira pergunta – Você tem access ao database em si? Isso deve ser normalizado no database, idealmente, caso contrário, qualquer solução estará sujeita a erros. Na minha experiência, campos de dados cheios de “OEM” e “CMB” tendem a acabar tendo coisas como “oem” e outros “dados de porcaria” misturados ao longo do tempo …. Se você puder normalizar, você pode usar a chave na tabela contendo os elementos como seu Enum, e pronto, com uma estrutura muito mais limpa.

Se isso não estiver disponível, eu faria seu Enum e faria uma aula para analisar sua string no Enum para você. Isso pelo menos lhe daria alguma flexibilidade no manuseio de inputs não-padrão e muito mais flexibilidade para trapping ou manipulação de erros do que fazer qualquer uma das soluções alternativas usando Enum.Parse / Reflection / etc. Um dictionary funcionaria, mas poderia quebrar se você tiver problemas no caso, etc.

Eu recomendo escrever uma aula para você fazer:

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

Isso preserva a maior parte de sua legibilidade sem ter que alterar o database.

Se bem entendi, você precisa de uma conversão de string para enum:

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

Você pode torná-lo mais sofisticado com genéricos para o tipo enum, se desejar.

No VS 2015, você pode usar o nome

 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)); 
 public class DataType { private readonly string value; private static readonly Dictionary 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(); 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; } } } 

Se você está tentando tornar seu código legível:

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

e se você precisar de uma lista deles, inclua essas finais em uma lista final estática <>. Este exemplo está em Java.

Se você estiver tentando tornar seu aplicativo legível, adicione:

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

enums em C # são restritas a tipos numéricos inteiros subjacentes (byte, sbyte, short, ushort, int, uint, long e ulong). Você não pode associá-los a um valor subjacente baseado em caractere ou string.

Uma abordagem diferente pode ser definir um dictionary do tipo Dictionary .

Esta é uma maneira de usá-lo como um parâmetro fortemente tipado ou como uma string :

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

Você pode usar dois enums. Um para o database e outro para legibilidade.

Você só precisa garantir que eles fiquem sincronizados, o que parece ser um custo pequeno. Você não precisa definir os valores, basta definir as posições da mesma forma, mas definir os valores torna muito claro que as duas enumerações estão relacionadas e impede que os erros reorganizem os membros da enumeração. E um comentário permite que a equipe de manutenção saiba que eles estão relacionados e devem ser mantidos em sincronia.

 // 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 usá-lo, basta converter para o código primeiro:

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

Então, se você quiser torná-lo ainda mais conveniente, você pode adicionar uma function de extensão que só funciona para este tipo de enum:

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

e você pode então apenas fazer:

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

Um pequeno ajuste para o método Glennular Extension, então você poderia usar a extensão em outras coisas que não apenas ENUM’s;

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

Ou usando o Linq

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

Eu estava basicamente procurando a resposta do Reflection by @ArthurC

Apenas para estender um pouco a sua resposta, você pode melhorar ainda mais tendo uma function genérica:

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

Então você pode apenas embrulhar o que você tem

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

ou

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

Baseado em outras opiniões, é isso que eu invento. Essa abordagem evita ter que digitar .Value onde você deseja obter o valor constante.

Eu tenho uma class base para todas as enums de string 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 } 

O JsonConverter é assim:

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

E um enum string real será algo assim:

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

E com isso, você pode apenas usar Color.Red para serializar até mesmo para json sem usar a propriedade Value

Eu até implementei alguns enums como sugerido por @Even (via class X e membros public static X ), apenas para descobrir mais tarde que atualmente, começando com o .Net 4.5, existe o método ToString() correto .

Agora estou reimplementando tudo de volta aos enums.

Eu não precisei de nada robusto como armazenar a string em atributos. Eu só precisava transformar algo como MyEnum.BillEveryWeek em “fatura toda semana” ou MyEnum.UseLegacySystem em “usar o sistema legado” – basicamente dividir o enum por seu invólucro de camelo em palavras minúsculas individuais.

 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() produz “use o sistema legado”

Se você tiver vários sinalizadores definidos, ele transformará isso em inglês simples (delimitado por vírgulas, exceto um “e” no lugar da última vírgula).

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

Eu fiz algo assim;

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

Chame isso com isso;

 BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS) 

Por que não apenas usar ToString () nos tipos Enum?

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