Asociar enumeraciones con cadenas en C #

363

Sé que lo siguiente no es posible porque el tipo de enumeración tiene que ser un int

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

De mi base de datos obtengo un campo con códigos incomprensivos (los OEMy CMBs). Me gustaría convertir este campo en enumalgo o algo más comprensible. Porque si el objetivo es la legibilidad, la solución debe ser breve.

¿Qué otras opciones tengo?

Boris Callens
fuente
posible duplicado de Enum ToString
nawfal
12
No estoy seguro de por qué la mayoría de las respuestas no solo usan "cadena constante" y en su lugar están haciendo clases personalizadas.
CTS_AE
1
Es posible que no pueda usar cadenas, pero puede usar caracteres muy bien. Esa es una opción si puede usar valores de una letra.
T. Sar
1
Genuinamente confundido sobre por qué la solución propuesta anteriormente por CTS_AE ni siquiera está en las tres respuestas principales.
Sinjai
@Sinjai La agrupación explícita de valores relacionados superará la penalización de una pérdida de rendimiento imperceptible, especialmente en una API o componente reutilizable.
persona27

Respuestas:

404

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 valores de cadena de tipo seguro 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);
Incluso Mien
fuente
44
El único inconveniente que se me ocurre es que sería un poco más lento, pero esto en la mayoría de los casos sería descuidado. Y no tendría exactamente el mismo comportamiento en el editor. EG: cambiar este no rellenaría automáticamente un caso para cada posibilidad. Aparte de esos puntos menores, creo que esta es probablemente una solución bastante simple.
Boris Callens
3
Y es fácil usar Dictionary <LogCategory, Action / Func> como un interruptor. :)
Arnis Lapsa
44
@ArnisL. No es suficiente trabajar como clave, debe anular Equals () y GetHashCode (), y desea que el establecedor de propiedades Value sea privado. Aún así, no es una enumeración.
Dave Van den Eynde
21
Para mi propio uso, amplié este concepto, anulando el ToStringmétodo para regresar Value. Y luego proporcionó operadores de conversión implícitos hacia y desde una cadena. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth
66
¿Qué hay de usar esto en casos de cambio?
David
175

También podría usar el modelo de extensión:

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

Su 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());
Glennular
fuente
3
Vea también stackoverflow.com/questions/4367723/… para otra extensión y de cadena a enumeración a modo de descripción.
Dave
15
¡No puedo evitar pensar que reflejar la enumeración cada vez que quieres mostrar el texto suena un poco doloroso desde una perspectiva de rendimiento!
Liath
44
@Liath - El `.ToString ()` ya usa la reflexión, por lo que realmente no está perdiendo nada con este enfoque y ganando visibilidad
James King
1
¿Podría hacer que la extensión sea genérica para que se aplique automáticamente a todas las enumeraciones?
erosebe
3
Para hacer genérico, use public static string ToDescriptionString(this Enum ...ie sin escribir explícitamente en MyEnum.
LeeCambl
100

¿Qué tal usar una clase estática con 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?
  }
}
revs rpattabi
fuente
99
Convenido. Tengo problemas para ver el propósito detrás de las soluciones más complejas, excepto quizás para poder cambiar la "enumeración" resultante.
fakeleft
@fakeleft no puede usar un tipo de clase estática con un tipo genérico (plantilla), y tal vez otras limitaciones, creo que es por eso que la gente prefiere las soluciones "más complejas".
eselk
2
Sin embargo
arviman
46
Los tipos estáticos no se pueden usar como parámetros.
Pedro Moreira
2
Como señala @PedroMoreira, no puede pasar GroupTypescomo un tipo de argumento porque es una clase estática. Ese es el problema que la respuesta de Even Mien resuelve. En este caso, tendría que tenerlo void DoSomething(string groupType), lo que significa que groupTypepodría tener cualquier valor de cadena , incluso valores que no esperaba, lo que significa que debe estar preparado para esos tipos no válidos y decidir cómo manejarlos (p. Ej. lanzando una excepción). Incluso la respuesta de Mien resuelve eso limitando el número de entradas válidas a las opciones definidas por la LogCategoryclase.
Pharap
30

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 reflexionaría sobre los campos estáticos del tipo de la enumeración (en este caso GroupTypes) y obtendría DescriptionAttributeel valor que estaba buscando utilizando 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 DescriptionAttributemismo arriba, en caso de que desee poder determinar si el atributo se aplica o no.

casperOne
fuente
Aunque recordaré esto para situaciones más complejas, es bastante complejo para una situación con el nivel de complejidad de lo que dije en el OP
Boris Callens
26

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 de enumeración, 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 cadena, ambas funcionan bastante bien, pero tiendo a preferir esto.

Arthur C
fuente
Estaba pensando lo mismo, pero debe haber alguna trampa en esto ... De lo contrario, sospecharía que más personas sugerirían esto (tal vez solo estoy paranoico).
Matthijs Wessels
Lo único que sé de esto es que creo que usa la reflexión para descubrir la cadena. Como resultado, si solo busco una solución para hacer un seguimiento de una cadena constante, normalmente usaré una clase para almacenar la mayoría de mis cadenas constantes. Sin embargo, si tengo una situación en la que una Enum es la solución correcta (independientemente de obtener una cadena descriptiva sobre mis elementos Enum), en lugar de tener una cadena adicional flotando en algún lugar para administrar, solo uso el valor enum como se describe.
Arthur C
+1 Esta es la respuesta mejor y más fácil, y también tiene votos altos aquí para probarlo. El único momento en que es mejor usar el modelo de extensión es cuando necesita espacios en el texto (más detalles aquí ).
SharpC
14
No, esto es solo obtener el nombre de un valor de enumeración, no asignar una cadena a un valor de enumeración. El objetivo del OP es tener una cadena diferente del valor de enumeración, por ejemplo: TheGroup = "OEM", TheOtherGroup = "CMB".
Tim Autin el
3
Estoy de acuerdo con el comentario de @ Tim, esto no es lo que el OP está tratando de hacer. Si se pregunta cuál es el caso de uso de esto, considere una situación en la que un dispositivo toma cadenas como comandos, pero también debe existir una versión "legible para humanos" del comando. Necesitaba esto para asociar algo como "Actualizar firmware" con el comando "UPDATEFW".
JYelton
20

Intente agregar constantes a una clase estática. No terminas con un tipo, pero tendrás constantes legibles y organizadas:

public static class GroupTypes {

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

}
darasd
fuente
3
Difícil pasar del código al nombre descriptivo. Tendría que usar la reflexión sobre todos los campos constantes para buscar una coincidencia.
andleer
1
@andleer No entiendo tu preocupación. Esta es la solución que uso.
VSO
Sí, esto es precisamente lo que quería. Y esta es la solución más concisa / elegante que veo, como si estuviera definiendo una enumeración con valores int, pero con valores de cadena en su lugar. 100% perfecto
Chad
3
El problema con esto es que no funciona como una enumeración en el sentido de que no tendremos un tipo separado con una lista finita de valores. Una función que espera esto podría usarse con cadenas de forma libre que es propensa a errores.
Juan Martínez
14

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 correcto de DBGroupTypes de las cadenas "OEM" y "CMB". Luego puede convertirlos a int y recuperar los valores correctos de la enumeración correcta que desea utilizar más en su modelo.

Dave Van den Eynde
fuente
Esto parece ser un paso adicional en el proceso, ¿por qué no una clase que maneja todo?
C. Ross
11
¿En lugar de usar atributos y reflexión?
Dave Van den Eynde
13

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.");
    }
}
Cruzar
fuente
1
Difícil pasar del código al nombre descriptivo. Tendría que usar la reflexión sobre todos los campos constantes para buscar una coincidencia.
andleer
1
Entiendo tu argumento. Subiré una versión que funciona muy bien más tarde, pero admito que es bastante pesada.
C. Ross
Mi versión basada en la solución de C. Ross stackoverflow.com/a/48441114/3862615
Roman M
7

Otra forma de lidiar con el problema es tener una enumeración y una serie de cadenas que mapearán los valores de enumeración con la lista de cadenas:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

puedes usarlo de esta manera:

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

Solicitará CMB

PROS:

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

CONTRAS:

  1. Propenso a estropear la lista al editarla, pero estará bien para una lista corta.
Luis Orantes
fuente
6

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 proviene 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());  
    }
}
azamsharp
fuente
5

¿Has considerado una tabla de búsqueda con 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.

Jim Mischel
fuente
¿Cómo se obtiene fácilmente la clave para un valor dado?
eglasius
La pregunta no pedía ir al otro lado. Pero sería lo suficientemente simple como para construir otro diccionario que vaya al revés. Es decir, Dictionary <GroupTypes, string>.
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; }
    }
}
Romana m
fuente
3

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 resultados aprobados / reprobados:

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

Lo convertiría en una clase y evitaría una enumeración por completo. Y luego, con el uso de un typehandler, 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"); }

}
Bryan Rowe
fuente
2

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

Editar: 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.

jhale
fuente
¿También puedes agarrar fácilmente una clave para un valor dado?
eglasius
Para C.Ross: no estoy seguro de lo que quieres decir. Puede leer los valores desde una base de datos y llenar dinámicamente el diccionario.
jhale
2

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 basura' mezclados a lo largo del tiempo ... Si puede normalizarlos, podría usar la clave en la tabla que contiene los elementos como Enum, y ya está, con una estructura mucho más limpia.

Si eso no está disponible, haría su Enum, y haría una clase para analizar su cadena en la Enum por usted. Esto al menos 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 usando Enum.Parse / Reflection / etc. Un diccionario funcionaría, pero podría romperse 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 conserva la mayor parte de su legibilidad sin tener que cambiar la base de datos.

Reed Copsey
fuente
2

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

Si lo desea, puede hacerlo más elegante con genéricos para el tipo de enumeración.

dmihailescu
fuente
2

En VS 2015, puede 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));
Claus Elmann
fuente
2

Un pequeño ajuste al método Glennular Extension, para que pueda usar la extensión en otras cosas que no sean 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;  
    }
}
Miguel
fuente
2

Siguiendo la respuesta de @Even Mien, he intentado ir un poco más allá y hacerlo genérico, parece que estoy casi allí, pero un caso aún resiste y probablemente pueda simplificar un poco mi código.
Lo publico aquí si alguien ve cómo podría mejorar y sobre todo hacer que funcione, ya que no puedo asignarlo desde una cadena

Hasta ahora tengo los siguientes 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 .... :(:(

Donde sucede la magia:

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



}

Solo entonces tengo que declarar esto para mis enumeraciones:

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"); } }
}
Lomithrani
fuente
Gracias por este gran trabajo, estuve buscando este enfoque por mucho tiempo. Creo que deberías obtener 1000 puntos por esto
user3492977
Oh, gracias por este comentario, y gracias por recordarme esto, no había usado c # durante dos años cuando escribí este fragmento de código, ¡debería volver pronto!
Lomithrani
@ user3492977 Finalmente volví a hacerlo y lo volví completamente funcional, aunque aún dudo si es una gran idea o algo inútil: D stackoverflow.com/questions/62043138/…
Lomithrani
2

Nuevo en .Net Core 3.0 / C # 8.0 (si su entorno de trabajo le permite actualizar su proyecto) es una declaración de cambio breve que parece algo enumista. Al final del día, es la misma declaración de cambio aburrida que hemos estado usando durante años.

La única diferencia real aquí es que la declaración de cambio tiene un nuevo traje.

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

Notarás que el código anterior que copié desde aquí , en realidad está usando una enumeración como parámetro.

No es exactamente lo que quieres (y confía en mí, he querido algo similar a lo que el OP está solicitando durante mucho tiempo), pero realmente siento que esto es algo así como una rama de olivo de MS. JMO

Espero que ayude a alguien!

usuario3180664
fuente
2

Usé una estructura como se aludió en una respuesta anterior, pero eliminé cualquier complejidad. Para mí, esto fue como crear una enumeración de cadenas. Se usa de la misma manera que se usa una enumeración.

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

Ejemplo de uso:

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

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

Ahora estoy reimplementando todo de nuevo a las enumeraciones.

bohdan_trotsenko
fuente
1

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");
}
Nicolas Bodin-Ripert
fuente
1

Puedes usar dos enumeraciones. Uno para la base de datos y el otro para facilitar la lectura.

Solo necesita asegurarse de que permanezcan sincronizados, lo que parece un pequeño costo. No es necesario establecer los valores, solo establecer las posiciones de la misma manera, pero establecer los valores deja 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 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, primero debe convertir al código:

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

Luego, si desea hacerlo 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 luego puedes hacer:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
toddmo
fuente
Esa es una mala práctica: con un dependiente, enumun cambio de valor previsto en uno podría estropear involuntariamente al otro.
Lorenz Lo Sauer
1

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

Solo para extender un poco su respuesta, puede hacerlo aún mejor si tiene 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 envolver lo que tengas

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

o

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

Tomado de @EvenMien y agregado en algunos de los comentarios. (También para mi propio 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(); }
}
moho
fuente
1

Agregar esta clase

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

Este trabajo está usando CallerMemberName para minimizar la codificación.

Utilizando:

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

Pruébalo:

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

salida:

ScannerDefaultFlashLight
Scanner1dCodes
nyconing
fuente
0

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 cadenas 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 una enumeración 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, puede usar Color.Red para incluso serializar a json sin usar la propiedad Value

ayepiz
fuente
0

No necesitaba nada robusto como almacenar la cadena en atributos. Solo necesitaba convertir algo así MyEnum.BillEveryWeeken "factura todas las semanas" o MyEnum.UseLegacySystemen "usar un 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() salidas "usar sistema heredado"

Si tiene varios indicadores configurados, lo convertirá en inglés simple (delimitado por comas excepto una "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"
Chad Hedgcock
fuente