Связывание перечислений со строками В C#
Я знаю, что следующее невозможно, потому что это должно быть int
enum GroupTypes
{
TheGroup = "OEM",
TheOtherGroup = "CMB"
}
из моей базы данных я получаю поле с непонятными кодами (OEM и CMB). Я хотел бы сделать это поле перечислением или чем-то еще понятным. Поскольку целью является читаемость, решение должно быть кратким.
Какие еще варианты у меня есть?
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
плюсы:
- легкий и чистый код.
- высокая производительность (особенно по сравнению с теми подходами, которые использует классы)
плюсы:
- склонен к испортить списке при редактировании, но это будет хорошо для короткий список.
вы рассматривали таблицу поиска с помощью словаря?
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 Xmembers), просто чтобы узнать позже, что в эти дни, начиная с .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