Строковое представление перечисления

у меня есть следующие перечисления:

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

проблема, однако, в том, что мне нужно слово "формы", когда я прошу AuthenticationMethod.Формы, а не id 1.

я нашел следующее решение этой проблемы (ссылке):

сначала мне нужно создать пользовательский атрибут под названием "StringValue":

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

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

    public string Value
    {
        get { return _value; }
    }

}

затем я могу добавить этот атрибут в мой перечислитель:

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

и конечно мне нужно что-то получать 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;
    }
}

хорошо, теперь у меня есть инструменты, чтобы получить строковое значение перечислителя. Затем я могу использовать его так:

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

хорошо, теперь все это работает как шарм, но я нахожу это много работы. Мне было интересно, есть ли лучшее решение для этого.

Я также попробовал что-то со словарем и статическими свойствами, но это тоже не было лучше.

7 ответов


попробовать type-safe-enum узор.

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

}

обновление Явное (или неявное) преобразование типов может быть выполнено с помощью

  • добавление статического поля с отображением

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • n.b. Чтобы инициализация полей" enum member "не вызывала исключение NullReferenceException при вызове конструктора экземпляра, обязательно поместите поле словаря перед полями "enum member" в ваш класс. Это связано с тем, что инициализаторы статических полей вызываются в порядке объявления и перед статическим конструктором, создавая странную и необходимую, но запутанную ситуацию, в которой конструктор экземпляра может быть вызван до инициализации всех статических полей и до вызова статического конструктора.
  • заполнение этого сопоставления в конструкторе экземпляра

    instance[name] = this;
    
  • и добавить пользовательский тип оператор преобразования

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

способ применения

Enum.GetName(Type MyEnumType,  object enumvariable)  

как в (Допустим Shipper определен перечислимый)

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

есть куча других статических методов в классе перечисления, которые также стоит исследовать...


вы можете ссылаться на имя, а не на значение, используя ToString ()

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

документация здесь:

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

...и если вы назовете свои перечисления в случае Паскаля (как я это делаю-например, ThisIsMyEnumValue = 1 и т. д.) тогда вы можете использовать очень простое регулярное выражение для печати дружественной формы:

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

который можно легко вызвать от любого строка:

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

выходы:

Преобразовать Мои Сумасшедшие Паскаль Делу Приговор Дружественные Дела

это экономит весь путь вокруг домов, создавая пользовательские атрибуты и прикрепляя их к перечислениям или используя таблицы поиска, чтобы выйти замуж за значение перечисления с дружественной строкой и лучше всего это самоуправление и может использоваться на любой строке случая Pascal, которая бесконечно более многоразовая. Конечно, это не позволяет вам иметь разные дружественное имя, чем ваше перечисление, которое предоставляет ваше решение.

мне нравится ваше оригинальное решение, хотя для более сложных сценариев. Вы можете сделать свое решение еще одним шагом и сделать GetStringValue методом расширения вашего перечисления, а затем вам не нужно будет ссылаться на него как на 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;
}

вы можете легко получить к нему доступ прямо из своего экземпляра перечисления:

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

к сожалению, отражение, чтобы получить атрибуты на перечисления довольно медленно:

см. Этот вопрос: кто-нибудь знает быстрый способ добраться до пользовательских атрибутов по значению перечисления?

на .ToString() довольно медленно на перечислениях тоже.

вы можете написать методы расширения для перечислений, хотя:

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

это не здорово, но будет быстрым и не потребует отражения атрибутов или имени Поля.


C#6 Update

если вы можете использовать C#6, то новый тег nameof оператор работает для перечисления, так nameof(MyEnum.WINDOWSAUTHENTICATION) будет преобразовано в "WINDOWSAUTHENTICATION" at время компиляции, что делает его самым быстрым способом получить имена перечислений.

обратите внимание, что это преобразует явное перечисление в встроенную константу, поэтому оно не работает для перечислений, которые у вас есть в переменной. Итак:

nameof(AuthenticationMethod.FORMS) == "FORMS"

но...

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

я использую метод расширения:

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

теперь украшают enum С:

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

когда вы называете

AuthenticationMethod.FORMS.ToDescription() вы получаете "FORMS".


просто использовать ToString() метод

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

для ссылки на строку Tomato, просто использовать

any.Tomato.ToString();

Я использую атрибут Description из системы.Пространство имен ComponentModel. Просто украсьте перечисление, а затем используйте этот код, чтобы получить его:

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

        }

пример:

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

этот код хорошо подходит для перечислений, где вам не нужно "дружественное имя" и вернет только.ToString () перечисления.