Associando enums com strings em C #


363

Eu sei que o seguinte não é possível porque o tipo da Enumeração deve ser um int

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

Do meu banco de dados eu recebo um campo com códigos incompreensível (os OEMe CMBs). Eu gostaria de transformar esse campo em enumalgo compreensível. Porque se o objetivo é legibilidade, a solução deve ser concisa.

Que outras opções eu tenho?


possível duplicata do Enum ToString
nawfal 8/13

12
Não sei por que a maioria das respostas não usa apenas "const string" e, em vez disso, está criando classes personalizadas.
CTS_AE

11
Você pode não conseguir usar strings, mas pode usar chars muito bem. Essa é uma opção se você pode usar valores de uma letra.
T. Sar

11
Verdadeiramente confuso sobre o motivo pelo qual a solução proposta acima pelo CTS_AE não está nem entre as três principais respostas.
Sinjai 5/09/17

O agrupamento explícito de valores relacionados do @Sinjai superará a penalidade de uma perda de desempenho imperceptível, especialmente em uma API ou componente reutilizável.
person27

Respostas:


404

Eu gosto de usar propriedades em uma classe em vez de métodos, pois elas parecem mais enum.

Aqui está um exemplo para um criador de logs:

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

4
A única desvantagem que posso sugerir é que seria um pouco mais lento, mas na maioria dos casos isso seria negligenciável. E não teria exatamente o mesmo comportamento no editor. EG: alternar sobre este não preencheria automaticamente um caso para cada possibilidade. Além desses pontos menores, acho que essa é provavelmente uma solução bastante simples.
Boris Callens 28/08/2009

3
E é fácil usar o Dictionary <LogCategory, Action / Func> como uma opção. :)
Arnis Lapsa

4
@ArnisL. Não basta trabalhar como chave, você precisa substituir Equals () e GetHashCode () e deseja tornar o configurador de propriedade Value privado. Ainda assim, não é um enum.
Dave Van den Eynde

21
Para meu próprio uso, ampliei esse conceito, substituindo o ToStringmétodo de retorno Value. E, em seguida, forneceu operadores de conversão implícitos de e para uma string. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth

6
Que tal usar isso em casos de switch?
David David

175

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

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

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

3
Consulte também stackoverflow.com/questions/4367723/… para outra extensão e de string para enum por meio de descrição.
Dave

15
Não consigo deixar de pensar que refletir o enum toda vez que você deseja exibir o texto parece meio doloroso do ponto de vista da performance!
Liath

4
@Liath - O `.ToString ()` já usa reflexão, para que você não está realmente perdendo nada com esta abordagem, e ganhando readibility
James Rei

11
Você poderia tornar a extensão genérica para que ela fosse aplicada automaticamente a todas as enums?
Erosebe 25/05

3
Para tornar genérico, use public static string ToDescriptionString(this Enum ...ie sem digitar explicitamente para MyEnum.
LeeCambl

100

Que tal usar uma classe estática com constantes?

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

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}

9
Acordado. Estou tendo problemas para entender o objetivo por trás das soluções mais complexas, exceto talvez para poder alternar o "enum" resultante.
Fakeleft 16/12

@fakeleft você não pode usar um tipo de classe estática com um tipo genérico (modelo), e talvez outras limitações, acho que é por isso que as pessoas preferem as soluções "mais complexas".
eselk

2
Porém

46
Tipos estáticos não podem ser usados ​​como parâmetros.
Pedro Moreira

2
Como o @PedroMoreira aponta, você não pode passar GroupTypescomo um tipo de argumento porque é uma classe estática. Esse é o problema que a resposta de Even Mien resolve. Nesse caso, você teria que ter void DoSomething(string groupType), o que significa que groupTypepoderia ter qualquer valor de string , mesmo valores que você não esperava, o que significa que você deve estar preparado para esses tipos inválidos e decidir como lidar com eles (por exemplo, lançando uma exceção). Até a resposta de Mien resolve isso limitando o número de entradas válidas às opções definidas pela LogCategoryclasse.
Pharap

30

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

Você precisaria usar o especificador "field" para aplicar os atributos, assim:

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

    [field:Description("CMB")]
    TheOtherGroup
}

Você refletiria sobre os campos estáticos do tipo de enumeração (neste caso, GroupTypes) e obteria DescriptionAttributeo valor que estava procurando usando a reflexão:

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

Optei por retornar o DescriptionAttributemesmo acima, caso você queira determinar se o atributo é ou não aplicado.


Embora eu vou lembrar disso para situações mais complexas, é bastante complexo para uma situação com o nível de complexidade do que afirmei no OP
Boris Callens

26

Você pode fazer isso com muita facilidade, na verdade. Use o seguinte código.

enum GroupTypes
{
   OEM,
   CMB
};

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

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

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


Eu estava pensando a mesma coisa, mas deve haver algum problema nisso ... Caso contrário, eu suspeitaria que mais pessoas sugeririam isso (talvez eu seja apenas paranóico).
Matthijs Wessels

O único problema que tenho conhecimento disso é que acredito que ele usa reflexão para descobrir a string. Como resultado, se estou procurando uma solução para acompanhar uma string constante, normalmente usarei uma classe para armazenar a maioria das minhas strings constantes. No entanto, se eu tiver uma situação em que um Enum é a solução certa (independentemente de obter uma sequência descritiva sobre meus elementos do Enum), em vez de ter uma sequência extra flutuando em algum lugar para gerenciar, apenas uso o valor de enum, conforme descrito.
Arthur C

+1 Esta é a melhor e mais fácil resposta, e também tem votos altos aqui para provar isso. O único momento em que é melhor usar o modelo de extensão é quando você precisa de espaços no texto (mais detalhes aqui ).
SharpC

14
Não, isso está apenas recebendo o nome de um valor de enumeração, não atribuindo uma string a um valor de enumeração. O objetivo do OP é ter uma sequência diferente do valor da enumeração, por exemplo: TheGroup = "OEM", TheOtherGroup = "CMB".
Tim Autin

3
Concordo com o comentário de @ Tim, não é isso que o OP está tentando fazer. Se você está se perguntando o que é um caso de uso, considere uma situação em que um dispositivo aceita seqüências de caracteres como comandos, mas é necessário que haja também uma versão "legível por humanos" do comando. Eu precisava disso para associar algo como "Atualizar firmware" ao comando "UPDATEFW".
precisa saber é o seguinte

20

Tente adicionar constantes a uma classe estática. Você não acaba com um Type, mas terá constantes organizadas legíveis:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}

3
Difícil voltar do código ao nome descritivo. Você precisaria usar a reflexão sobre todos os campos const para procurar uma correspondência.
andleer

11
@andleer Não entendo sua preocupação. Esta é a solução que eu uso.
VSO 15/05

Sim, isso é exatamente o que eu queria. E esta é a solução mais concisa / elegante que eu vejo, como se estivesse definindo uma enumeração com valores int - mas com valores de string. 100% perfeito.
Chad

3
O problema disso é que ele não funciona como um Enum, no sentido de que não teremos um tipo separado com uma lista finita de valores. Uma função que espera que isso possa ser usada com cadeias de forma livre, sujeitas a erros.
Juan Martinez

14

Crie uma segunda enumeração para o seu banco de dados que contenha 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 convertê-los em int e recuperar os valores corretos da enumeração correta que deseja usar mais em seu modelo.


Esta parece ser uma etapa extra no processo, por que não uma classe que lida com tudo?
1033 C. C. Ross

11
Ao contrário de usar atributos e reflexão?
Dave Van den Eynde 16/03/2009

13

Use uma classe.

Edit: Melhor exemplo

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

11
Difícil voltar do código ao nome descritivo. Você precisaria usar a reflexão sobre todos os campos const para procurar uma correspondência.
andleer

11
Eu entendo o seu ponto. Vou carregar uma versão que funcione mais tarde, mas admito que é bastante pesada.
1155 C. C. Ross

Minha versão com base na solução de C. Ross stackoverflow.com/a/48441114/3862615
Roman M

7

Outra maneira de lidar com o problema é ter uma enumeração e uma matriz de sequências que mapeiam os valores da enumeração com a lista de sequências:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

você pode usar algo assim:

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

Isso solicitará ao CMB

PROS:

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

CONTRAS:

  1. É provável que você estrague a lista ao editá-la, mas tudo bem para uma lista curta.

6

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

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

O atributo Descrição 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 a enumeração como valor da sequência 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());  
    }
}

5

Você considerou uma tabela de pesquisa usando um dicionário?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// 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 a ler.


Como você obtém facilmente a chave para um determinado valor?
eglasius

A pergunta não pediu para ir para o outro lado. Mas seria simples o suficiente criar outro dicionário que fosse o contrário. Ou seja, Dictionary <GroupTypes, string>.
21711 Jim Mischel

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

3

O C # não suporta seqüências de caracteres enumeradas, mas na maioria das situações você pode usar uma Lista ou Dicionário para obter o efeito desejado.

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

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

2

Eu faria parte de uma classe e evitaria um enum completamente. E, com o uso de um manipulador de tipos, você pode criar o objeto quando o pegar no banco de dados.

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

}

2

Gostaria apenas de criar um dicionário 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 classe para lidar com isso.


Além disso, você pode facilmente pegar uma chave para um determinado valor?
eglasius

Para C.Ross - não sei o que você quer dizer. Você pode ler os valores em um banco de dados e preencher dinamicamente o dicionário.
21139 jhale

2

Minha primeira pergunta - Você tem acesso ao próprio banco de dados? Isso deve ser normalizado no banco de dados; 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 com coisas como "oem" e outros 'dados de merda' misturados ao longo do tempo ... Se você pode normalizá-lo, pode usar a tecla na tabela que contém os elementos como seu Enum e pronto, com uma estrutura muito mais limpa.

Se isso não estiver disponível, eu criaria seu Enum e faria uma classe para analisar sua string no Enum para você. Isso proporcionaria, pelo menos, alguma flexibilidade no tratamento de entradas não padrão e muito mais flexibilidade para capturar ou manipular erros do que executar qualquer uma das soluções alternativas usando o Enum.Parse / Reflection / etc. Um dicionário funcionaria, mas poderia ser quebrado se você tiver problemas com o caso, etc.

Eu recomendo escrever uma classe para que você possa:

// 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 da sua legibilidade sem precisar alterar o banco de dados.


2

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 de enum, se desejar.


2

No VS 2015, você pode usar o nome de

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

2

Um pequeno ajuste no método Glennular Extension, para que você possa usar a extensão em outras coisas que não apenas 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;
        }
    }
}

Ou usando o 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;  
    }
}

2

Seguindo a resposta de @Even Mien, tentei ir um pouco mais longe e torná-lo genérico, parece que estou quase lá, mas um caso ainda resiste e provavelmente posso simplificar um pouco meu código.
Eu posto aqui se alguém ver como eu poderia melhorar e, especialmente, fazê-lo funcionar, pois não posso atribuí-lo a partir de uma string

Até agora, tenho os seguintes resultados:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

Onde as mágicas acontecem:

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

Só então tenho que declarar isso para minhas enumerações:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}

Obrigado por este excelente trabalho, eu estava procurando por essa abordagem por um longo tempo. Eu acho que você deve obter 1000 pontos por isso
user3492977 24/03

Oh, obrigado por este comentário e obrigado por me lembrar disso, eu não usava c # há dois anos quando escrevi esse pedaço de código, devo voltar em breve!
Lomithrani 25/03

@ user3492977 Finalmente voltei a fazê-lo e o tornei totalmente funcional, ainda estou em dúvida se é uma ótima idéia ou uma coisa inútil: D stackoverflow.com/questions/62043138/…
Lomithrani

2

Novo no .Net Core 3.0 / C # 8.0 (se o seu ambiente de trabalho permitir que você atualize seu projeto) é uma instrução de troca abreviada que parece um pouco enumerada. No final do dia, é a mesma declaração antiga de chave chata que usamos há anos.

A única diferença real aqui é que a declaração do switch ganhou um novo terno.

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

Você notará que o código acima do qual copiei daqui está realmente usando uma enumeração como parâmetro.

Não é exatamente o que você deseja (e confie em mim, eu queria algo parecido com o que o OP está solicitando há muito tempo), mas eu realmente sinto que isso é um ramo de oliveira da MS. JMO.

Espero que ajude alguém!


2

Eu usei uma estrutura como mencionado em uma resposta anterior, mas acabei com qualquer complexidade. Para mim, isso foi mais como criar uma enumeração de strings. É usado da mesma maneira que uma enumeração é usada.

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

Exemplo de uso:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }

1

Eu até implementei algumas enumerações, como sugerido por @Even (via class Xe public static Xmembros), apenas para descobrir mais tarde que hoje em dia, a partir do .Net 4.5, existe o direito ToString() método .

Agora estou reimplementando tudo de volta para enumerações.


1

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

1

Você pode usar duas enumerações. Um para o banco de dados e outro para facilitar a leitura.

Você só precisa garantir que eles fiquem sincronizados, o que parece ser um pequeno custo. 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 evita que erros reorganizem os membros da enumeração. E um comentário informa à equipe de manutenção que estes 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 primeiro no código:

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

Então, se você quiser torná-lo ainda mais conveniente, poderá adicionar uma função de extensão que funcione apenas para este tipo de enum:

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

e você pode então fazer:

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

Essa é uma prática ruim: com um dependente, enumuma alteração de valor pretendida em um pode atrapalhar o outro de maneira não intencional.
Lorenz Lo Sauer

1

Eu estava basicamente procurando pela resposta de Reflexão de @ArthurC

Apenas para estender um pouco sua resposta, você pode torná-la ainda melhor tendo uma função 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);
    } 

Então você pode simplesmente embrulhar o que tiver

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

ou

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

1

Retirado de @EvenMien e adicionado em alguns dos comentários. (Também para o meu próprio caso de uso)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString()
    {
        return this.Value;
    }

    public static AgentAction Login = new AgentAction("Logout");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}

1

Adicionando esta classe

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

Este trabalho está usando CallerMemberName para minimizar a codificação

Usando:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

Teste-o:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

resultado:

ScannerDefaultFlashLight
Scanner1dCodes

0

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

Eu tenho uma classe 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 de string real será algo como isto:

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é json sem usar a propriedade Value


0

Eu não precisava de nada robusto como armazenar a string em atributos. Eu só precisava transformar algo como MyEnum.BillEveryWeek"projeto de lei toda semana" ou MyEnum.UseLegacySystem"usar sistema legado" - basicamente dividi o enum por seu revestimento 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() saídas "usar sistema legado"

Se você tiver vários sinalizadores definidos, ele o transformará em inglês simples (delimitado por vírgula, 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"
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.