将枚举与C#中的string关联起来

我知道以下是不可能的,因为它必须是一个整数

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

从我的数据库中,我得到一个代码不全面的领域(OEM和CMB)。 我想把这个领域变成一个枚举或其他可以理解的东西。 由于目标是可读性,解决scheme应该是简洁的。
我还有什么其他的select?

我喜欢在类中使用属性而不是方法,因为它们看起来更像枚举。

这是一个logging器的例子:

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

传入types安全的string值作为参数:

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

您可以将属性添加到枚举中的项目,然后使用reflection从属性中获取值。

您将不得不使用“字段”说明符来应用这些属性,如下所示:

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

然后,您将反思枚举types的静态字段(本例中为GroupTypes),并使用reflection获取您正在查找的值的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(); } 

我select返回上面的DescriptionAttribute本身,如果你想能够确定属性是否应用。

创build第二个枚举,为您的数据库包含以下内容:

 enum DBGroupTypes { OEM = 0, CMB = 1 } 

现在,您可以使用Enum.Parse从string“OEM”和“CMB”中检索正确的DBGroupTypes值。 然后,可以将它们转换为int,并从要在模型中进一步使用的正确枚举中检索正确的值。

你可以很容易地做到这一点。 使用下面的代码。

 enum GroupTypes { OEM, CMB }; 

然后,当你想获得每个枚举元素的string值只使用下面的代码行。

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

过去我已经成功地使用了这个方法,而且我也使用了一个常量类来保存​​string常量,两者都工作得很好,但我倾向于这样做。

使用一个class级。

编辑:更好的例子

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

这里是我用来获取枚举值作为string的扩展方法。 首先是枚举。

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

现在,您可以使用以下代码作为string值访问枚举:

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

尝试将常量添加到静态类。 你最终不会得到一个types,但你有可读的,有组织的常量:

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

通过回答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](); } 

C#不支持枚举string,但对于大多数情况下,您可以使用List或Dictionary来获得所需的效果。

如要打印通过/失败结果:

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

你有没有考虑使用字典查找表?

 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()在读取时查找string。

我会把它变成一个class,完全避免一个枚举。 然后通过使用一个types处理程序,您可以在从数据库中获取对象时创build该对象。

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

我只是创build一个字典,并使用代码作为关键。

编辑:为了解决有关做反向查找(find关键)的评论,这不会是非常有效的。 如果这是必要的,我会写一个新的类来处理它。

我的第一个问题 – 你有没有访问数据库本身? 这应该在数据库中进行规范化,理想情况下,任何解决scheme都将容易出错。 根据我的经验,充满“OEM”和“CMB”的数据字段往往会随着时间的推移而混杂着像“oem”和其他“垃圾数据”混杂在一起的东西….如果可以规范化,可以使用密钥在包含元素作为Enum的表中,你完成了,结构更清洁。

如果这不可用,我会让你的枚举,并build立一个类来parsing你的string到你的Enum。 这至less会给你一些处理非标准条目的灵活性,并且比使用Enum.Parse / Reflection /等方法做任何变通办法都更灵活。 一本字典可以工作,但如果你有任何案例问题,可能会崩溃。

我build议你写一个类,这样你就可以做:

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

这可以保留大部分可读性,而无需更改数据库。

如果我理解正确,你需要从string转换为枚举:

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

如果你愿意的话,你可以使用enumtypes的generics。

如果你想让你的代码可读:

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

C#中的枚举仅限于底层整数值types(byte,sbyte,short,ushort,int,uint,long和ulong)。 您不能将它们与基于字符或string的基础值相关联。

一种不同的方法可能是定义一个Dictionary <string,string>types的字典。

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

说你有一个枚举如下:

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

这样做后,如果你想要的string值而不是int(索引),只是做.ToString(),你会得到的价值:

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

上面的语句会输出TheGroup而不是索引(int)。

希望这有助于解决您的问题。

这是一种将其用作强types参数或string的方法

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

那么如果你想使它更方便,你可以添加一个扩展函数,只适用于这种types的枚举:

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

然后你可以这样做:

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

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

根据其他意见,这是我想出的。 这种方法避免了input.Value你想得到的常量值。

我有这样的所有string枚举的基类:

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

而一个实际的string枚举将是这样的:

 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属性

我甚至实现了@Even(通过class Xpublic static X成员)build议的几个枚举,只是为了后来发现,现在开始.NET 4.5,有正确的 ToString()方法。

现在我将所有的东西都重新实现枚举。

我不需要任何健壮的东西像存储string属性。 我只是需要把MyEnum.BillEveryWeek这样的MyEnum.BillEveryWeek变成“每周MyEnum.UseLegacySystem ”或MyEnum.UseLegacySystem变成“使用遗留系统” – 基本上把它的骆驼套pipe分成单独的小写字母。

 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" 

为什么不使用Enumtypes的ToString()呢?

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