Связывание перечислений со строками в C #


363

Я знаю, что следующее невозможно, потому что тип перечисления должен быть int

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

Из моей базы данных я получаю поле с непонятными кодами ( OEMи CMB). Я хотел бы превратить это поле вenum понятным или чем-то еще. Потому что, если целью является читабельность, решение должно быть кратким.

Какие еще варианты у меня есть?


возможный дубликат Enum ToString
nawfal

12
Я не уверен, почему в большинстве ответов используется не просто "const string", а вместо этого они создают собственные классы.
CTS_AE

1
Возможно, вы не сможете использовать строки, но вы можете использовать символы просто отлично. Это вариант, если вы можете использовать однобуквенные значения.
Т. Сар

1
Искренне запутанный вопрос, почему решение, предложенное выше CTS_AE, даже не входит в тройку лучших ответов.
Синджай,

@Sinjai Явная группировка связанных значений перевесит штраф за незаметную потерю производительности, особенно в API или компоненте многократного использования.
персона 27

Ответы:


405

Мне нравится использовать свойства в классе вместо методов, так как они выглядят как перечисление.

Вот пример для Logger:

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

Передайте в качестве параметра строковые безопасные значения :

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

Применение:

Logger.Write("This is almost like an enum.", LogCategory.Info);

4
Единственным недостатком, который я могу придумать, является то, что это будет чуть-чуть медленнее, но в большинстве случаев этим можно пренебречь. И это не будет иметь такое же поведение в редакторе. Например: переключение на это не будет автоматически заполнять регистр для каждой возможности. Помимо этих незначительных моментов, я думаю, что это, вероятно, довольно простое решение.
Борис Калленс

3
И легко использовать Dictionary <LogCategory, Action / Func> в качестве переключателя. :)
Арнис Лапса

4
@ArnisL. Недостаточно работать в качестве ключа, вам нужно переопределить Equals () и GetHashCode (), и вы хотите сделать настройщик свойства Value закрытым. Тем не менее, это не перечисление.
Дэйв Ван ден Эйнде

21
Для собственного использования я расширил эту концепцию, переопределив ToStringметод return Value. И затем предоставил неявные операторы приведения к и из строки. public static implicit operator String(LogCategory category) { return Value; },
Зарепет

6
Как насчет использования этого в случаях переключения?
Дэвид

176

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

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

Ваш дополнительный класс

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

Применение:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());

3
См. Также stackoverflow.com/questions/4367723/… для другого расширения и от строки к enum в порядке описания.
Дэйв

15
Я не могу не думать, что отображение перечисления каждый раз, когда вы хотите отображать текст, кажется довольно болезненным с точки зрения производительности!
Лиат

4
@Liath - `.ToString ()` уже использует отражение, так что вы не теряете ничего при таком подходе и получаете удобочитаемость
Джеймс Кинг,

1
Не могли бы вы сделать расширение универсальным, чтобы оно автоматически применялось ко всем перечислениям?
erosebe

3
Чтобы сделать общий, используйте public static string ToDescriptionString(this Enum ...т.е. без явного ввода MyEnum.
LeeCambl

100

Как насчет использования статического класса с константами?

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
Согласовано. У меня проблемы с пониманием цели, стоящей за более сложными решениями, за исключением, может быть, возможности переключиться с полученного «перечисления».
fakeleft

@fakeleft вы не можете использовать статический тип класса с универсальным типом (шаблоном) и, возможно, другими ограничениями, я думаю, именно поэтому люди предпочитают «более сложные» решения.
Eselk

2
Для того чтобы это работало, константы должны быть внутренними или публичными
arviman

46
Статические типы нельзя использовать в качестве параметров.
Педро Морейра

2
Как указывает @PedroMoreira, вы не можете передавать GroupTypesв качестве типа аргумента, потому что это статический класс. Эту проблему решает даже Эвен. В этом случае вам вместо этого придется иметь void DoSomething(string groupType), что означает, что groupTypeможет иметь любое строковое значение , даже значения, которые вы не ожидаете, что означает, что вы должны быть готовы к этим недопустимым типам и решить, как с ними обращаться (например, бросая исключение). Даже ответ Миена решает это, ограничивая количество допустимых входных данных параметрами, определенными LogCategoryклассом.
Pharap

30

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

Вы должны использовать спецификатор "field" для применения атрибутов, например так:

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

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

Затем вы бы отразили статические поля типа перечисления (в данном случае GroupTypes) и получили DescriptionAttributeбы значение, которое вы искали, используя отражение:

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

Я решил вернуть DescriptionAttributeсаму себя выше, в случае, если вы хотите иметь возможность определить, применяется ли атрибут или нет.


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

26

Вы можете сделать это очень легко на самом деле. Используйте следующий код.

enum GroupTypes
{
   OEM,
   CMB
};

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

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

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


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

Единственный улов, который я знаю об этом, заключается в том, что я считаю, что он использует отражение, чтобы выяснить строку. В результате, если я просто ищу решение для отслеживания константной строки, то я обычно использую класс для хранения большинства моих константных строк. Однако, если у меня есть ситуация, когда Enum является правильным решением (независимо от получения описательной строки о моих элементах Enum), тогда вместо того, чтобы иметь дополнительную строку, плавающую где-то для управления, я просто использую значение enum, как описано.
Артур С

+1 Это самый лучший и самый простой ответ, а также имеет высокие голоса здесь, чтобы доказать это. Единственный раз, когда лучше использовать модель расширения, это когда вам нужны пробелы в тексте (подробнее здесь ).
SharpC

14
Нет, это просто получение имени значения перечисления, а не присвоение строки значению перечисления. Цель OP - иметь строку, отличную от значения перечисления, например: TheGroup = "OEM", TheOtherGroup = "CMB".
Тим Отин

3
Я согласен с комментарием @ Тим, это не то, что пытается сделать ОП. Если вам интересно, каков вариант использования этого, рассмотрите ситуацию, когда устройство принимает строки в качестве команд, но также должна быть и «удобочитаемая» версия команды. Мне нужно было связать что-то вроде «Обновить прошивку» с командой «UPDATEFW».
Джелтон

20

Попробуйте добавить константы в статический класс. Вы не получите Type, но у вас будут читабельные, упорядоченные константы:

public static class GroupTypes {

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

}

3
Сложно перейти от кода обратно к описательному названию. Вы должны будете использовать отражение над всеми константными полями для поиска совпадения.
andleer

1
@ andleer Я не понимаю твою озабоченность. Это решение, которое я использую.
VSO

Да, это на самом деле именно то, что я хотел. И это наиболее сжатое / элегантное решение, которое я вижу, как если бы я определял перечисление со значениями w / int, но вместо этого со строковыми значениями. 100% идеально.
Чад,

3
Проблема в том, что он не работает как Enum в том смысле, что у нас не будет отдельного типа с конечным списком значений. Функция, ожидающая их, может использоваться со строками произвольной формы, которые подвержены ошибкам.
Хуан Мартинес

14

Создайте второе перечисление для вашей БД, содержащее следующее:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Теперь вы можете использовать Enum.Parse для получения правильного значения DBGroupTypes из строк «OEM» и «CMB». Затем вы можете преобразовать их в int и получить правильные значения из правильного перечисления, которое вы хотите использовать в своей модели.


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

11
В отличие от использования атрибутов и отражения?
Дейв Ван ден Эйнде

13

Используйте класс.

Изменить: лучший пример

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

1
Сложно перейти от кода обратно к описательному названию. Вы должны будете использовать отражение над всеми константными полями для поиска совпадения.
andleer

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

Моя версия основана на решении К. Росса stackoverflow.com/a/48441114/3862615
Roman M

7

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

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

Вы можете использовать это примерно так:

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

Это подскажет CMB

ПЛЮСЫ:

  1. Простой и чистый код.
  2. Высокая производительность (особенно по сравнению с теми подходами, которые использует классы)

МИНУСЫ:

  1. Склонен перепутать список при редактировании, но это будет хорошо для короткого списка.

6

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

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

Атрибут Description произошел от System.ComponentModel.

И вот мой метод расширения:

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

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

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}

5

Рассматривали ли вы таблицу поиска, используя словарь?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

Затем вы можете использовать GroupTypeLookup.TryGetValue () для поиска строки при ее чтении.


Как вы легко можете получить ключ для данного значения?
eglasius

Вопрос не спрашивал идти другим путем. Но было бы достаточно просто создать другой словарь, который идет другим путем. То есть словарь <GroupTypes, string>.
Джим Мишель

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

C # не поддерживает перечисляемые строки, но для большинства ситуаций вы можете использовать List или Dictionary, чтобы получить желаемый эффект.

Например, для распечатки результатов прохождения / провала:

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

2

Я сделал бы это в классе, чтобы избежать перечисления вообще. И затем с использованием обработчика типов вы можете создать объект, когда вы берете его из БД.

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

Я бы просто создал словарь и использовал код в качестве ключа.

Редактировать: чтобы ответить на комментарий о обратном поиске (поиске ключа), это было бы не очень эффективно. Если это необходимо, я бы написал новый класс для обработки.


Также вы можете легко получить ключ для данного значения?
eglasius

Для К.Росса - я не уверен, что вы имеете в виду. Вы можете прочитать значения из БД и динамически заполнить словарь.
jhale

2

Мой первый вопрос - есть ли у вас доступ к самой базе данных? Это должно быть нормализовано в базе данных, в идеале, в противном случае любое решение будет подвержено ошибкам. По моему опыту, поля данных, заполненные "OEM" и "CMB", имеют тенденцию к тому, что со временем будут смешиваться такие вещи, как "oem" и другие "дерьмовые данные" ... Если вы можете их нормализовать, вы можете использовать ключ в таблице, содержащей элементы, такие как Enum, и все готово, с гораздо более чистой структурой.

Если это недоступно, я создам ваш Enum и создам класс для анализа вашей строки в Enum для вас. Это, по крайней мере, даст вам некоторую гибкость при обработке нестандартных записей и гораздо большую гибкость при перехвате или обработке ошибок, чем при выполнении любого из обходных путей с использованием Enum.Parse / Reflection / etc. Словарь будет работать, но может сломаться, если у вас возникнут проблемы с регистром и т. Д.

Я бы порекомендовал написать класс, чтобы вы могли сделать:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

Это сохраняет большую часть вашей читабельности без необходимости изменения БД.


2

Если я правильно понимаю, вам нужно преобразование из строки в 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");

При желании вы можете сделать его более подходящим с обобщениями для типа enum.


2

В VS 2015 вы можете использовать nameof

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

Применение:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));

2

Небольшая настройка метода Glennular Extension, так что вы можете использовать расширение не только в 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;
        }
    }
}

Или используя 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

Следуя ответу @Even Mien, я попытался пойти немного дальше и сделать его Generic. Кажется, я почти готов, но один случай все еще не решен, и я, вероятно, могу немного упростить свой код.
Я выкладываю это здесь, если кто-нибудь увидит, как я могу улучшить и особенно заставить его работать, так как я не могу назначить его из строки

Пока у меня есть следующие результаты:

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

Где происходит волшебство:

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



}

Я только тогда должен объявить это для моих перечислений:

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

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

О, спасибо за этот комментарий, и спасибо, что напомнили мне об этом, я не использовал c # в течение двух лет, когда писал этот фрагмент кода, я должен скоро вернуться к нему!
Ломитрани

@ user3492977 Я наконец вернулся к этому и сделал его полностью функциональным, я все еще сомневаюсь, если это отличная идея или бесполезная вещь: D stackoverflow.com/questions/62043138/…
Lomithrani

2

Новое в .Net Core 3.0 / C # 8.0 (если ваша рабочая среда позволяет вам обновить ваш проект) - это краткая инструкция переключения, которая выглядит несколько перечисляемой. В конце концов, это то же самое скучное заявление о переключении, которое мы использовали годами.

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

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

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

Это не совсем то, что вы хотите (и, поверьте мне, я давно хотел чего-то похожего на то, что запрашивает OP), но на самом деле я чувствую, что это нечто вроде оливковой ветви от MS. СМЭ.

Надеюсь, это поможет кому-то!


2

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

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

Пример использования:

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

1

Я даже реализовал несколько перечислений по предложению @Even (через class Xи public static Xучастников), чтобы потом выяснить, что в эти дни, начиная с .Net 4.5, есть право ToString() метод.

Теперь я перевоплощаю все обратно в перечисления.


1

Это способ использовать его как строго типизированный параметр или как строку :

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

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

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

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

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

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

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

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

и вы можете просто сделать:

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

Это плохая практика: с зависимым enumизменением предполагаемого значения одного может непреднамеренно испортить другого.
Лоренц Ло Зауэр

1

Я в основном искал ответ Reflection от @ArthurC

Просто чтобы немного расширить его ответ, вы можете сделать его еще лучше, используя универсальную функцию:

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

Тогда вы можете просто обернуть все, что у вас есть

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

или

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

1

Взято из @EvenMien и добавлено в некоторых комментариях. (Также для моего собственного случая использования)

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

Добавление этого класса

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

Эта работа использует CallerMemberName для минимизации кодирования

С помощью:

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

Попробуй это:

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

вывод:

ScannerDefaultFlashLight
Scanner1dCodes

0

Исходя из других мнений, это то, что я придумал. Этот подход избавляет от необходимости вводить .Value там, где вы хотите получить постоянное значение.

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

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
}

JsonConverter выглядит так:

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

И фактическое перечисление строк будет примерно таким:

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) { }
}

И с этим, вы можете просто использовать Color.Red даже для сериализации в JSON без использования свойства Value


0

Мне не нужно ничего надежного, как хранение строки в атрибутах. Мне просто нужно было превратить что-то вроде MyEnum.BillEveryWeekв «счет каждую неделю» или MyEnum.UseLegacySystemв «использовать устаревшую систему» ​​- по сути, разделить перечисление по верблюжьей оболочке на отдельные строчные слова.

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() выходы "использовать устаревшую систему"

Если у вас установлено несколько флагов, он превратит это в обычный английский (с разделителями-запятыми, кроме «и» вместо последней запятой).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.