Associating enums with strings in C#
我知道下面是不可能的,因为它必须是int
1 2 3 4 5 | enum GroupTypes { TheGroup ="OEM", TheOtherGroup ="CMB" } |
从我的数据库中,我得到了一个代码不全面的字段(原始设备制造商和商业银行)。我希望将此字段转换为枚举或其他可以理解的内容。因为目标是可读性,所以解决方案应该简洁。我还有其他的选择吗?
我喜欢在类中使用属性而不是方法,因为它们看起来更像枚举。
以下是一个日志记录器的示例:
1 2 3 4 5 6 7 8 9 10 11 12 | 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"); } } } |
将类型安全字符串值作为参数传入:
1 2 3 4 5 | public static void Write(string message, LogCategory logCategory) { var log = new LogEntry { Message = message }; Logger.Write(log, logCategory.Value); } |
用途:
1 | Logger.Write("This is almost like an enum.", LogCategory.Info); |
您还可以使用扩展模型:
1 2 3 4 5 6 7 | public enum MyEnum { [Description("String 1")] V1= 1, [Description("String 2")] V2= 2 } |
你的扩展类
1 2 3 4 5 6 7 8 9 10 11 | 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; } } |
用途:
1 2 | MyEnum myLocal = MyEnum.V1; print(myLocal.ToDescriptionString()); |
使用带有常量的静态类怎么样?客户端代码与枚举看起来没有什么不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 | static class GroupTypes { public const string TheGroup ="OEM"; public const string TheOtherGroup ="CMB"; } void DoSomething(GroupTypes groupType) { if(groupType == GroupTypes.TheOtherGroup) { //Launch nuclear bomb } } |
可以向枚举中的项添加属性,然后使用反射从属性中获取值。
您必须使用"字段"说明符来应用属性,如:
1 2 3 4 5 6 7 8 | enum GroupTypes { [field:Description("OEM")] TheGroup, [field:Description("CMB")] TheOtherGroup } |
然后,您将反映枚举类型的静态字段(在本例中是grouptypes),并获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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(); } |
我选择返回上面的
实际上你可以很容易地做到。使用以下代码。
1 2 3 4 5 | enum GroupTypes { OEM, CMB }; |
然后,当您想要获取每个枚举元素的字符串值时,只需使用下面的代码行。
1 |
在过去我成功地使用了这个方法,并且我还使用了一个常量类来保存字符串常量,这两个方法都很好地解决了问题,但是我倾向于使用这个方法。
为包含以下内容的数据库创建第二个枚举:
1 2 3 4 5 | enum DBGroupTypes { OEM = 0, CMB = 1 } |
现在,您可以使用enum.parse从字符串"oem"和"cmb"中检索正确的dbgrouptypes值。然后您可以将它们转换为int,并从您希望在模型中进一步使用的正确枚举中检索正确的值。
使用一个类。
编辑:更好的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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 2 3 4 5 | public static class GroupTypes { public const string TheGroup ="OEM"; public const string TheOtherGroup ="CMB" } |
解决此问题的另一种方法是使用枚举和字符串数组,该数组将枚举值与字符串列表映射:
1 2 3 4 5 6 7 8 9 10 | public enum GroupTypes { TheGroup = 0, TheOtherGroup } string[] GroupTypesStr = { "OEM", "CMB" }; |
您可以这样使用它:
16会提示招商银行
赞成的意见:
欺骗:
这里是我用来获取枚举值作为字符串的扩展方法。首先是枚举。
16description属性来自system.componentmodel。
下面是我的扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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(); } } |
现在,可以使用以下代码以字符串值的形式访问枚举:
1 2 3 4 5 6 7 8 9 | [TestFixture] public class when_getting_value_of_enum { [Test] public void should_get_the_value_as_string() { Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString()); } } |
你考虑过使用字典的查阅表格吗?
1 2 3 4 5 6 7 8 9 10 | 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()在读取字符串时查找该字符串。
C不支持枚举字符串,但在大多数情况下,您可以使用列表或字典来获得所需的效果。
例如,打印通过/失败结果:
1 2 3 | List<string> PassFail = new List<string> {"FAIL","PASS" }; bool result = true; Console.WriteLine("Test1:" + PassFail[result.GetHashCode()]); |
我只需要创建一个字典并使用代码作为键。
编辑:要处理有关进行反向查找(查找键)的注释,这不会非常有效。如果有必要,我将编写一个新类来处理它。
我的第一个问题-您有权访问数据库本身吗?这应该在数据库中进行规范化,理想情况下,否则任何解决方案都将容易出错。根据我的经验,充满"原始设备制造商"和"商业银行"的数据域往往会随着时间的推移而混合"原始设备制造商"和其他"垃圾数据"之类的东西……如果您可以规范化它,那么您可以使用包含元素的表中的键作为枚举,这样就完成了,结构更干净了。
如果这不可用,我将生成您的枚举,并生成一个类来将您的字符串解析为枚举。与使用enum.parse/reflection/etc执行任何解决方法相比,这至少会给您处理非标准项的灵活性,以及捕获或处理错误的灵活性。字典可以工作,但如果遇到案例问题,则可能会崩溃。
我建议你写一门课,这样你就可以:
1 2 | // I renamed this to GroupType, since it sounds like each element has a single type... GroupType theType = GroupTypeParser.GetGroupType(theDBString); |
这可以在不更改数据库的情况下保留大部分可读性。
我会把它变成一个类,完全避免枚举。然后,通过使用typehandler,可以在从数据库中获取对象时创建该对象。
IE:
1 2 3 4 5 6 7 8 |
对glennular扩展方法做了一个小调整,这样您就可以在其他事情上使用扩展,而不仅仅是枚举的扩展;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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; } } } |
如果我理解正确,您需要从字符串转换为枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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"); |
如果愿意的话,可以使用枚举类型的泛型使其更具花哨性。
我甚至按照@even(通过
现在我将所有的东西重新实现为枚举。
你可以用两个枚举。一个用于数据库,另一个用于可读性。
你只需要确保它们保持同步,这看起来成本很低。您不必设置值,只需设置相同的位置,但是设置值可以非常清楚地看到两个枚举是相关的,并防止错误重新排列枚举成员。一条评论让维修人员知道这些是相关的,必须保持同步。
1 2 3 4 5 6 7 8 9 10 11 12 13 | // keep in sync with GroupTypes public enum GroupTypeCodes { OEM, CMB } // keep in sync with GroupTypesCodes public enum GroupTypes { TheGroup = GroupTypeCodes.OEM, TheOtherGroup = GroupTypeCodes.CMB } |
要使用它,只需先转换为代码:
1 2 | GroupTypes myGroupType = GroupTypes.TheGroup; string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString(); |
然后,如果要使其更方便,可以添加只适用于此枚举类型的扩展函数:
1 2 3 4 | public static string ToString(this GroupTypes source) { return ((GroupTypeCodes)source).ToString(); } |
然后你可以做:
1 2 | GroupTypes myGroupType = GroupTypes.TheGroup; string valueToSaveIntoDatabase = myGroupType.ToString(); |
在vs 2015中,您可以使用
1 2 3 4 5 6 7 8 | public class LogCategory { public static string Trace; public static string Debug; public static string Info; public static string Warning; public static string Error; } |
用途:
1 |
这是一种将其用作强类型参数或字符串的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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"); } |
我基本上是在寻找@arthurc的反省答案
只需稍微扩展一下他的答案,您就可以通过使用一个通用函数使其变得更好:
1 2 3 4 5 6 7 8 9 10 11 | // 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); } |
然后你就可以把你有的东西包起来
1 | EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part |
或
1 | EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic |
我想完全避免使用字符串文字,而且在项目描述中也不需要空间。更重要的是,我希望有一个机制来检查提供的字符串是否是有效的项,因此我提出了这个解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Seasons { public static string Spring { get; } public static string Summer { get; } public static string Fall { get; } public static string Winter { get; } public static bool IsValid(string propertyName) { if (string.IsNullOrEmpty(propertyName)) { return false; } try { return typeof(Seasons).GetProperty(propertyName) != null; } catch { return false; } } } |
它的工作原理如下:
1 2 3 4 5 6 7 8 | void Main() { string s = nameof(Seasons.Fall); Console.WriteLine($"Fall is valid: {Seasons.IsValid(s)}"); // true s ="WrongSeason"; Console.WriteLine($"WrongSeason is valid: {Seasons.IsValid(s)}"); // false } |
我试图将isvalid()重构为基类,并使用反射来读取类型(methodBase.getCurrentMethod().DeclaringType),但由于我希望它是静态的,所以它返回的是基类类型,而不是继承的类型。您对此的治疗将非常欢迎!以下是我试图实现的目标:
25根据其他观点,这就是我想出来的。这种方法避免了在要获取常量值的地方输入.value。
我有一个这样的所有字符串枚举的基类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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()); } } } |
实际的字符串枚举如下所示:
1 2 3 4 5 6 7 8 |
有了这个,您就可以使用color.red,甚至可以在不使用value属性的情况下序列化到JSON。
我不需要像在属性中存储字符串这样强大的东西。我只需要把像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } |
如果设置了多个标志,它会将其转换为纯英语(逗号分隔,但"and"替代最后一个逗号)。
1 2 3 4 | var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes; Console.WriteLine(myCustomerBehaviour.UnCamelCase()); //outputs"bill every week, use legacy system and charge taxes" |
我做过这样的事;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } } } |
用这个称呼它;
1 | BusinessUnitService.StringBusinessUnits(BusinessUnits.PARTS) |