getname - строковое перечисление c#




Строковое представление Enum (20)

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

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

Проблема, однако, в том, что мне нужно слово «FORMS», когда я запрашиваю AuthenticationMethod.FORMS, а не идентификатор 1.

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

Сначала мне нужно создать пользовательский атрибут под названием «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);

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

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


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

Если вы этого не сделаете и просто нуждаетесь в перечислении типа string (который не является интегральным типом, поэтому он не может быть базой перечисления), вот способ:

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

вы можете использовать тот же синтаксис, что и перечисление, чтобы ссылаться на него

if (bla == AuthenticationMethod.FORMS)

Он будет немного медленнее, чем с числовыми значениями (сравнение строк вместо чисел), но с плюсовой стороны он не использует отражение (медленное) для доступа к строке.


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

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

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

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

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

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

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

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

Выходы:

Преобразование моего сумасшедшего случая Паскаля в дружественный случай

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

Мне действительно нравится ваше оригинальное решение, хотя для более сложных сценариев. Вы можете сделать свое решение еще на один шаг и сделать 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());

Использовать метод

Enum.GetName(Type MyEnumType,  object enumvariable)  

как в (Предположите, что Shipper - это определенный Enum)

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

В классе Enum есть еще множество статических методов, которые тоже нужно исследовать ...


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

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

.ToString() довольно медленный для перечислений.

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

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

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

Обновление C # 6

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

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

nameof(AuthenticationMethod.FORMS) == "FORMS"

Но...

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

Как я решил это как метод расширения:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

Enum:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

Использование (где o.OrderType - свойство с тем же именем, что и перечисление):

o.OrderType.GetDescription()

Это дает мне строку «Новая карта» или «Перезагрузка» вместо фактического значения перечисления NewCard и Refill.


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

  • Являются ли имена моих значений перечисления достаточно дружественными для этой цели или мне нужны более дружественные?
  • Нужно ли мне туда и обратно? То есть, мне нужно будет принимать текстовые значения и анализировать их в значениях перечисления?
  • Это что-то, что мне нужно сделать для многих перечислений в моем проекте или только одного?
  • Какими элементами пользовательского интерфейса я буду представлять эту информацию, в частности, буду ли я привязываться к пользовательскому интерфейсу или использовать листы свойств?
  • Должно ли это быть локализуемым?

Самый простой способ сделать это - Enum.GetValue (и поддерживать Enum.Parse отключение с использованием Enum.Parse ). Также часто стоит TypeConverter , как предлагает Стив Митчем, для поддержки привязки интерфейса. (Нет необходимости создавать TypeConverter когда вы используете листы свойств, что является одной из приятных вещей о листах свойств. Хотя лорд знает, что у них есть свои проблемы).

В общем, если ответы на вышеуказанные вопросы подскажут, что это не сработает, моим следующим шагом будет создание и заполнение статического Dictionary<MyEnum, string> или, возможно, Dictionary<Type, Dictionary<int, string>> . Я имею тенденцию пропускать промежуточный этап decorate-the-code-with-attributes, потому что то, что обычно происходит на щуке, это необходимость изменения дружественных значений после развертывания (часто, но не всегда, из-за локализации).


Мой вариант

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

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

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

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


Очень простое решение для этого с .Net 4.0 и выше. Никакой другой код не требуется.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

Чтобы получить строку только для использования:

MyStatus.Active.ToString("f");

или же

MyStatus.Archived.ToString("f");`

Значение будет «Активно» или «Архивировано».

Чтобы увидеть различные строковые форматы («f» сверху) при вызове Enum.ToString просмотрите страницу « Строки формата перечисления»


Просто используйте метод ToString()

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

Чтобы ссылаться на строку « Tomato , просто используйте

any.Tomato.ToString();

Я использую атрибут Description из пространства имен System.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 () перечисления.


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

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" .


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

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

должно быть

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

Brillant!


Я согласен с Кейтом, но я не могу проголосовать (пока).

Я использую статический метод и инструкцию swith, чтобы вернуть именно то, что я хочу. В базе данных я храню tinyint, и мой код использует только фактическое перечисление, поэтому строки предназначены для требований пользовательского интерфейса. После многочисленных испытаний это привело к лучшей производительности и большему контролю над выходом.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

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


Вот еще один способ выполнить задачу связывания строк с перечислениями:

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

Этот метод называется следующим:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

Вы можете группировать связанные перечисления в своей собственной структуре. Поскольку этот метод использует тип перечисления, вы можете использовать Intellisense для отображения списка перечислений при совершении GetString()вызова.

Вы можете дополнительно использовать новый оператор в DATABASEструктуре. Не использовать это означает, что строки Listне выделяются до первого GetString()вызова.


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

  1. Используется в инструкции switch, например switch (myEnum)
  2. Может использоваться в функциональных параметрах, например foo (myEnum type)
  3. Можно ссылаться, например, myEnum.FirstElement
  4. Я могу использовать строки, например foo («FirstElement») == foo (myEnum.FirstElement)

1,2 и 4 действительно могут быть решены с помощью C # Typedef строки (поскольку строки переключаются в c #)

3 могут быть решены статическими константными строками. Поэтому, если у вас одинаковые потребности, это самый простой подход:

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

Это позволяет, например:

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

а также

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Где CreateType можно вызывать со строкой или типом. Однако недостатком является то, что любая строка автоматически является допустимым перечислением , это может быть изменено, но тогда для этого потребуется какая-то функция init ... или, возможно, сделать их явным литом внутренним?

Теперь, если значение int было важно для вас (возможно, для скорости сравнения), вы могли бы использовать некоторые идеи от Jakub Šturc фантастический ответ и сделать что-то немного сумасшедшее, это мой удар на него:

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

но, конечно, «Типы bob = 4;» было бы бессмысленным, если бы вы не инициализировали их сначала, что бы сорвать точку ...

Но в теории TypeA == TypeB будет быстрее ...


Мой ответ, работающий над ответом @ user29964 (который, безусловно, является самым простым и самым близким к Enum)

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

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

        public string Value
        {
            get { return _value; }
        }



        public static string GetStringValue(Enum Flagvalue)
        {
            Type type = Flagvalue.GetType();
            string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();
            List<string> values = new List<string>();

            for (int i = 0; i < flags.Length; i++)
            {

                FieldInfo fi = type.GetField(flags[i].ToString());

                StringValue[] attrs =
                   fi.GetCustomAttributes(typeof(StringValue),
                                           false) as StringValue[];
                if (attrs.Length > 0)
                {
                    values.Add(attrs[0].Value);
                }
            }
            return String.Join(",", values);

        }

использование

[Flags]
    public enum CompeteMetric
    {

        /// <summary>
        /// u
        /// </summary>
        [StringValue("u")]//Json mapping
        Basic_UniqueVisitors = 1 //Basic
             ,
        /// <summary>
        /// vi
        /// </summary>
        [StringValue("vi")]//json mapping
        Basic_Visits = 2// Basic
            ,
        /// <summary>
        /// rank
        /// </summary>
        [StringValue("rank")]//json mapping
        Basic_Rank = 4//Basic
 }

пример

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
        string strmetrics = StringValue.GetStringValue(metrics);

это вернет «vi, rank»


для меня прагматичный подход - это класс внутри класса, образец:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }

Если вы думаете о проблеме, которую мы пытаемся решить, это не переименование, которое нам нужно вообще. Нам нужен объект, который позволяет связать определенное количество значений с eachother; другими словами, определить класс.

Якуб Šturc типа безопасного перечислить шаблон является лучшим вариантом, который я вижу здесь.

Посмотри на это:

  • Он имеет частный конструктор, поэтому только сам класс может определять допустимые значения.
  • Это запечатанный класс, поэтому значения не могут быть изменены через наследование.
  • Он безопасен по типу, позволяя вашим методам требовать только этот тип.
  • Отсутствие эффекта отражения, вызванного доступом к значениям.
  • И, наконец, его можно изменить, чтобы связать более двух полей вместе, например, Имя, Описание и числовое значение.


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

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

Теперь, если мы попробуем вызвать вышеупомянутый метод, мы можем назвать это таким образом

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

Все, что вам нужно сделать, это просто создать файл ресурсов, содержащий все значения перечислителя и соответствующие строки

Resource Name          Resource Value
Color_Red              My String Color in Red
Color_Blue             Blueeey
Color_Green            Hulk Color

Что действительно очень приятно в том, что это будет очень полезно, если вам нужно, чтобы ваше приложение было локализованным, поскольку все, что вам нужно сделать, это просто создать другой файл ресурсов с новым языком! и Voe-la!





enums