关于.net:将枚举与C#中的字符串相关联

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),并获取DescriptionAttribute,以获取您要使用reflection查找的值:

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

我选择返回上面的DescriptionAttribute本身,如果您希望能够确定是否应用了该属性。


实际上你可以很容易地做到。使用以下代码。

1
2
3
4
5
enum GroupTypes
{
   OEM,
   CMB
};

然后,当您想要获取每个枚举元素的字符串值时,只需使用下面的代码行。

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

在过去我成功地使用了这个方法,并且我还使用了一个常量类来保存字符串常量,这两个方法都很好地解决了问题,但是我倾向于使用这个方法。


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

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

会提示招商银行

赞成的意见:

  • 简单明了的代码。
  • 高性能(特别是与那些使用类)
  • 欺骗:

  • 编辑时容易把列表弄乱,但是短名单。

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

    16

    description属性来自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
    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"); }

    }

    对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(通过class Xpublic static X成员)的建议实现了一些枚举,只是为了稍后发现,从.NET 4.5开始,有正确的ToString()方法。

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


    你可以用两个枚举。一个用于数据库,另一个用于可读性。

    你只需要确保它们保持同步,这看起来成本很低。您不必设置值,只需设置相同的位置,但是设置值可以非常清楚地看到两个枚举是相关的,并防止错误重新排列枚举成员。一条评论让维修人员知道这些是相关的,必须保持同步。

    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
    Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));

    这是一种将其用作强类型参数或字符串的方法:

    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
    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,甚至可以在不使用value属性的情况下序列化到JSON。


    我不需要像在属性中存储字符串这样强大的东西。我只需要把像MyEnum.BillEveryWeek这样的东西变成"每周账单",或者把MyEnum.UseLegacySystem变成"使用遗留系统"——基本上用驼色大小写将枚举拆分成单独的小写单词。

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

    MyEnum.UseLegacySystem.UnCamelCase()输出"使用遗留系统"

    如果设置了多个标志,它会将其转换为纯英语(逗号分隔,但"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)