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

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

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

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

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

Ответ 1

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

Вот пример для 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);

Ответ 2

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

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

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

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

Ответ 4

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

Вам нужно будет использовать спецификатор "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, если вы хотите определить, применяется ли этот атрибут даже.

Ответ 5

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

enum GroupTypes
{
   OEM,
   CMB
};

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

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

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

Ответ 6

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

public static class GroupTypes {

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

}

Ответ 7

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

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

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

Ответ 8

Использовать класс.

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

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

Ответ 9

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

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

Атрибут Описание появился в 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());  
    }
}

Ответ 10

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

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

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

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

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

ПЛЮСЫ:

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

МИНУСЫ:

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

Ответ 11

Считаете ли вы справочную таблицу с использованием словаря?

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() для поиска строки при ее прочтении.

Ответ 12

Ответа на этот вопрос Even:

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 void Foo(LogCategory logCategory){    

  var @switch = new Dictionary<LogCategory, Action>{
    {LogCategory.Trace, ()=>Console.Writeline("Trace selected!")},
    {LogCategory.Debug, ()=>Console.Writeline("Debug selected!")},
    {LogCategory.Error, ()=>Console.Writeline("Error selected!")}};

   //will print one of the line based on passed argument
  @switch[logCategory]();
}

Ответ 13

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

например. Чтобы распечатать результаты прохода/сбоя:

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

Ответ 14

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

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

Ответ 15

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

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

}

Ответ 16

Мой первый вопрос: есть ли у вас доступ к самой базе данных? Это должно быть нормализовано в базе данных, в идеале, в противном случае любое решение будет подвержено ошибкам. По моему опыту, поля данных, полные "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);

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

Ответ 17

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

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

Ответ 18

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

Ответ 19

Небольшая настройка метода 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;  
    }
}

Ответ 20

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

Ответ 21

перечисления в С# ограничены базовыми целыми числовыми типами (байт, sbyte, short, ushort, int, uint, long и ulong). Вы не можете связать их с базовым значением, основанным на символах или строках.

Другой подход может заключаться в определении словаря типа Dictionary < string, string > .

Ответ 22

Если вы пытаетесь сделать свой код доступным для чтения:

class GroupTypes {
    public static final String (whatever oem stands for) = "OEM";
    public static final String (whatever cmb stands for) = "CMB";
    ...
}

и если вам нужен список из них, включите эти финалы в статическом финальном списке <String> . Этот пример приведен в Java.

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

public static final Map<String, String> groupsByDbValue;
static {
    groupsByDbValue = new HashMap<String, String>();
    groupsByDbValue.put("OEM", "(whatever OEM stands for)");
    groupsByDbValue.put("CMB", "(whatever CMB stands for)");
}

Ответ 23

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

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

Ответ 24

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

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

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

Ответ 25

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

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

Ответ 26

Я в основном искал ответ 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

Ответ 27

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

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

Ответ 28

Основываясь на других мнениях, это то, что я придумал. Этот подход позволяет избежать необходимости вводить значение .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

Ответ 29

Мне не нужно было ничего надежного, как сохранение строки в атрибутах. Мне просто нужно было превратить что-то вроде 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"

Ответ 30

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

enum GroupTypes
{
    [Description("TheGroup")]
    TheGroup,
    [Description("TheOtherGroup")]
    TheOtherGroup
}

После этого, если вы хотите, чтобы строковое значение вместо int (index), просто сделайте .ToString(), и вы получите значение:

Debug.WriteLine(GroupTypes.TheGroup.ToString());

Выше инструкция выведет TheGroup NOT index (int).

Надеюсь, это поможет решить вашу проблему.