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



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



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


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

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

500   30  

30 ответов:

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

вот пример для регистратора:

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

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

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

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

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(GroupTypes groupType)
{
  if(groupType == GroupTypes.TheOtherGroup)
  {
    //Launch nuclear bomb 
  }  
}

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

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

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

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

enum GroupTypes
{
   OEM,
   CMB
};

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

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

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

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

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

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

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

редактировать: лучший пример

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

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

public static class GroupTypes
{
    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB"
}

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

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

атрибут описания пришел из системы.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());  
    }
}

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

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

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

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

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

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

плюсы:

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

плюсы:

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

вы рассматривали таблицу поиска с помощью словаря?

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 (), чтобы посмотреть строку, когда вы ее читаете.

ответ даже:

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

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

например, для печати результатов pass / fail:

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

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

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

}

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

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

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

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

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

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

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

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

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

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

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

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

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

и если вам нужен список из них, включите эти финалы в статический окончательный список. Этот пример в 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)");
}

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

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

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

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

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

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

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

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

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

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

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

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

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

и с этим, вы можете просто использовать цвет.Красный, чтобы даже сериализовать в json без использования свойства Value

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

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

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

Я сделал что-то вроде этого;

public enum BusinessUnits
{
    NEW_EQUIPMENT = 0,
    USED_EQUIPMENT = 1,
    RENTAL_EQUIPMENT = 2,
    PARTS = 3,
    SERVICE = 4,
    OPERATOR_TRAINING = 5
}

public class BusinessUnitService
{
    public static string StringBusinessUnits(BusinessUnits BU)
    {
        switch (BU)
        {
            case BusinessUnits.NEW_EQUIPMENT: return "NEW EQUIPMENT";
            case BusinessUnits.USED_EQUIPMENT: return "USED EQUIPMENT";
            case BusinessUnits.RENTAL_EQUIPMENT: return "RENTAL EQUIPMENT";
            case BusinessUnits.PARTS: return "PARTS";
            case BusinessUnits.SERVICE: return "SERVICE";
            case BusinessUnits.OPERATOR_TRAINING: return "OPERATOR TRAINING";
            default: return String.Empty;
        }
    }
}

вызовите его с этим;

BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS)

почему бы просто не использовать ToString () на перечисление типов?

private enum LogType { Error, Warning, Info};

private WriteLog(string message, LogType logType)
{
    Log.Write(message, logType.ToString());
}

Comments

    Ничего не найдено.