Pregunta Representación de cadena de un Enum


Tengo la siguiente enumeración:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

Sin embargo, el problema es que necesito la palabra "FORMULARIOS" cuando solicito AuthenticationMethod.FORMS y no el id 1.

He encontrado la siguiente solución para este problema (enlazar)

Primero necesito crear un atributo personalizado llamado "StringValue":

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

Entonces puedo agregar este atributo a mi enumerador:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

Y, por supuesto, necesito algo para recuperar ese StringValue:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

Bueno, ahora tengo las herramientas para obtener un valor de cadena para un enumerador. Puedo usarlo así:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

De acuerdo, ahora todo esto funciona como un encanto, pero me parece mucho trabajo. Me preguntaba si hay una mejor solución para esto.

También probé algo con un diccionario y propiedades estáticas, pero tampoco fue mejor.


823
2018-01-08 14:15


origen


Respuestas:


Tratar type-safe-enum patrón.

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Actualizar La conversión explícita (o implícita) se puede hacer mediante

  • agregar campo estático con mapeo

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • nótese bien. Para que la inicialización de los campos "miembro enum" no arroje una NullReferenceException al llamar al constructor de la instancia, asegúrese de colocar el campo Diccionario antes de los campos "miembro enum" en su clase. Esto se debe a que los inicializadores de campo estáticos se llaman en orden de declaración y antes del constructor estático, creando la situación extraña y necesaria pero confusa que se puede invocar al constructor de instancia antes de que se hayan inicializado todos los campos estáticos y antes de llamar al constructor estático.
  • llenando este mapeo en el constructor de instancias

    instance[name] = this;
    
  • y agregando operador de conversión de tipo definido por el usuario

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }
    

814
2018-01-08 14:29



Método de uso

Enum.GetName(Type MyEnumType,  object enumvariable)  

como en (Asumir Shipper es un Enum definido)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Hay muchos otros métodos estáticos en la clase Enum que vale la pena investigar también ...


209
2018-01-08 14:19



Puede hacer referencia al nombre en lugar del valor utilizando ToString ()

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

La documentación está aquí:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... y si nombra sus enumeraciones en Pascal Case (como lo hago, como ThisIsMyEnumValue = 1 etc.), entonces podría usar una expresión regular muy simple para imprimir la forma amigable:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

que puede ser llamado fácilmente desde cualquier cadena:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Productos:

Convierta mi frase de caso de Pascal loco a caso amistoso

Eso ahorra correr todo el camino alrededor de las casas creando atributos personalizados y adjuntarlos a tus enumeraciones o usando tablas de búsqueda para unir un valor de enumeración con una cadena amigable y lo mejor de todo es que se maneja automáticamente y se puede usar en cualquier cadena de Pascal Case que sea infinitamente más reutilizable Por supuesto, no te permite tener un diferente nombre amigable que su enumeración que proporciona su solución.

Aunque me gusta tu solución original para escenarios más complejos. Podrías llevar tu solución un paso más allá y convertir tu GetStringValue en un método de extensión de tu enumeración y luego no necesitarías hacer referencia a él como StringEnum.GetStringValue ...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

A continuación, puede acceder fácilmente desde su instancia enum:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

74
2018-01-08 14:20



Lamentablemente, la reflexión para obtener atributos en las enumeraciones es bastante lenta:

Ver esta pregunta: ¿Alguien sabe una manera rápida de llegar a los atributos personalizados en un valor enum?

los .ToString() es bastante lento en enumeraciones también.

Sin embargo, puedes escribir métodos de extensión para enums:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

Esto no es genial, pero será rápido y no requerirá el reflejo de los atributos o el nombre del campo.


C # 6 Actualización

Si puede usar C # 6, entonces el nuevo nameof el operador trabaja para enumeraciones, entonces nameof(MyEnum.WINDOWSAUTHENTICATION) se convertirá en "WINDOWSAUTHENTICATION" a tiempo de compilación, por lo que es la forma más rápida de obtener nombres enum.

Tenga en cuenta que esto convertirá la enumeración explícita a una constante en línea, por lo que no funciona para las enumeraciones que tiene en una variable. Asi que:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Pero...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

63
2018-02-11 00:15



Yo uso un método de extensión:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

Ahora decora el enum con:

public enum AuthenticationMethod
{
    [Description("FORMS")]
    FORMS = 1,
    [Description("WINDOWSAUTHENTICATION")]
    WINDOWSAUTHENTICATION = 2,
    [Description("SINGLESIGNON ")]
    SINGLESIGNON = 3
}

Cuando usted llama

AuthenticationMethod.FORMS.ToDescription() conseguirás "FORMS".


52
2017-09-06 15:50



Solo usa el ToString() método

public enum any{Tomato=0,Melon,Watermelon}

Para hacer referencia a la cadena Tomato, Solo usa

any.Tomato.ToString();

36
2018-01-08 14:30



Uso el atributo Descripción del espacio de nombres System.ComponentModel. Simplemente decore la enumeración y luego use este código para recuperarla:

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
        {
            Type type = enumerationValue.GetType();
            if (!type.IsEnum)
            {
                throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
            }

            //Tries to find a DescriptionAttribute for a potential friendly name
            //for the enum
            MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0)
                {
                    //Pull out the description value
                    return ((DescriptionAttribute)attrs[0]).Description;
                }
            }
            //If we have no description attribute, just return the ToString of the enum
            return enumerationValue.ToString();

        }

Como ejemplo:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

Este código es ideal para enumeraciones donde no necesita un "nombre descriptivo" y devolverá solo .ToString () de la enumeración.


26
2017-09-22 20:46