简体   繁体   English

C# 获取枚举值

[英]C# Getting Enum values

I have a enum containing the following (for example):我有一个包含以下内容的枚举(例如):

  • UnitedKingdom,英国,
  • UnitedStates,美国,
  • France,法国,
  • Portugal葡萄牙

In my code I use Country.UnitedKingdom but I want to have the value be UK if I assign it to a string for example.在我的代码中,我使用Country.UnitedKingdom ,但如果我将其分配给一个字符串,我希望该值为UK

Is this possible?这可能吗?

You can't assign an enum value to a string to start with.您不能将枚举值分配给字符串作为开头。 You'd have to call ToString() , which would convert Country.UnitedKingdom to "UnitedKingdom".您必须调用ToString() ,这会将Country.UnitedKingdom转换为“UnitedKingdom”。

Two options suggest themselves:有两种选择:

  • Create a Dictionary<Country, string>创建Dictionary<Country, string>
  • A switch statement开关语句
  • Decorate each value with an attribute, and load that with reflection用属性装饰每个值,并用反射加载它

Comments about each of them...关于他们每个人的评论......

Sample code for Dictionary<Country,string> Dictionary<Country,string>的示例代码

using System;
using System.Collections.Generic;

enum Country
{
    UnitedKingdom, 
    UnitedStates,
    France,
    Portugal
}

class Test
{
    static readonly Dictionary<Country, string> CountryNames =
        new Dictionary<Country, string>
    {
        { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
    };

    static string ConvertCountry(Country country) 
    {
        string name;
        return (CountryNames.TryGetValue(country, out name))
            ? name : country.ToString();
    }

    static void Main()
    {
        Console.WriteLine(ConvertCountry(Country.UnitedKingdom));
        Console.WriteLine(ConvertCountry(Country.UnitedStates));
        Console.WriteLine(ConvertCountry(Country.France));
    }
}

You might want to put the logic of ConvertCountry into an extension method.您可能希望将ConvertCountry的逻辑放入扩展方法中。 For example:例如:

// Put this in a non-nested static class
public static string ToBriefName(this Country country) 
{
    string name;
    return (CountryNames.TryGetValue(country, out name))
        ? name : country.ToString();
}

Then you could write:然后你可以写:

string x = Country.UnitedKingdom.ToBriefName();

As mentioned in the comments, the default dictionary comparer will involve boxing, which is non-ideal.如评论中所述,默认字典比较器将涉及装箱,这是不理想的。 For a one-off, I'd live with that until I found it was a bottleneck.对于一次性的,我会接受它,直到我发现它是一个瓶颈。 If I were doing this for multiple enums, I'd write a reusable class.如果我对多个枚举执行此操作,我会编写一个可重复使用的 class。

Switch statement开关语句

I agree with yshuditelu's answer suggesting using a switch statement for relatively few cases.我同意yshuditelu 的回答,建议在相对较少的情况下使用switch语句。 However, as each case is going to be a single statement, I'd personally change my coding style for this situation, to keep the code compact but readable:然而,由于每个案例都将是一个单一的陈述,我个人会针对这种情况改变我的编码风格,以保持代码紧凑但可读:

public static string ToBriefName(this Country country) 
{
    switch (country)
    {
        case Country.UnitedKingdom:  return "UK";
        case Country.UnitedStates:   return "US";
        default:                     return country.ToString();
    }
}

You can add more cases to this without it getting too huge, and it's easy to cast your eyes across from enum value to the return value.您可以向其添加更多案例,而不会变得太大,并且很容易将您的目光从枚举值转移到返回值。

DescriptionAttribute

The point Rado made about the code for DescriptionAttribute being reusable is a good one, but in that case I'd recommend against using reflection every time you need to get a value. Rado关于DescriptionAttribute代码可重用的观点很好,但在那种情况下,我建议不要在每次需要获取值时都使用反射。 I'd probably write a generic static class to hold a lookup table (probably a Dictionary , possibly with a custom comparer as mentioned in the comments).我可能会写一个通用的 static class 来保存一个查找表(可能是一个Dictionary ,可能带有评论中提到的自定义比较器)。 Extension methods can't be defined in generic classes, so you'd probably end up with something like:扩展方法不能在泛型类中定义,所以你可能会得到类似这样的结果:

public static class EnumExtensions
{
    public static string ToDescription<T>(this T value) where T : struct
    {
        return DescriptionLookup<T>.GetDescription(value);
    }

    private static class DescriptionLookup<T> where T : struct
    {
        static readonly Dictionary<T, string> Descriptions;

        static DescriptionLookup()
        {
            // Initialize Descriptions here, and probably check
            // that T is an enum
        }

        internal static string GetDescription(T value)
        {
            string description;
            return Descriptions.TryGetValue(value, out description)
                ? description : value.ToString();
        }
    }
}

I prefer to use the DescriptionAttribute on my enums.我更喜欢在我的枚举上使用DescriptionAttribute Then, you can use the following code to grab that description from the enum.然后,您可以使用以下代码从枚举中获取该描述。

enum MyCountryEnum
{    
    [Description("UK")]
    UnitedKingdom = 0,    

    [Description("US")]
    UnitedStates = 1,    

    [Description("FR")]
    France = 2,    

    [Description("PO")]
    Portugal = 3
}

public static string GetDescription(this Enum value)
{
    var type = value.GetType();

    var fi = type.GetField(value.ToString());

    var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];

    return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}

public static SortedDictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
    // validate.
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("T must be an Enum type.");
    }

    var results = new SortedDictionary<string, T>();

    FieldInfo[] fieldInfos = typeof(T).GetFields();

    foreach (var fi in fieldInfos)
    {

        var value = (T)fi.GetValue(fi);
        var description = GetDescription((Enum)fi.GetValue(fi));

        if (!results.ContainsKey(description))
        {
            results.Add(description, value);
        }
    }
    return results;
}

And then to get my bound enum list, its simply a call to然后要获取我的绑定枚举列表,只需调用

GetBoundEnum<MyCountryEnum>()

To get a single enum's description, you'd just use the extension method like this要获得单个枚举的描述,您只需使用这样的扩展方法

string whatever = MyCountryEnum.UnitedKingdom.GetDescription();

You could create an extension method public static string ToShortString(this Country country) .您可以创建一个扩展方法public static string ToShortString(this Country country) In the method you could use either a static Dictionary as Jon suggests, or you could simply do a switch case.在该方法中,您可以按照 Jon 的建议使用 static 字典,或者您可以简单地做一个 switch case。

Example:例子:

public static class CountryExtensions
{
    public static string ToShortString( this Country target )
    {
        switch (target) {
            case Country.UnitedKingdom:
                return "UK";
            case Country.UnitedStates:
                return "US";
            case Country.France:
                return "FR";
            case Country.Portugal:
                return "PT";
            default:
                return "None";
        }
    }
}

Pseudo code:伪代码:

enum MyCountryEnum
{
    UnitedKingdom = 0,
    UnitedStates = 1,
    France = 2,
    Portugal = 3,
}

string[] shortCodes = new string[] {"UK", "US", "FR", "PO"};


MyCountryEnum enumValue = MyCountryEnum.UnitedKingdom;
string code = shortCodes[enumValue];

One other possibility that hasn't been mentioned is something like this:另一种没有提到的可能性是这样的:

public class Country
{
    public static readonly Country UnitedKingdom = new Country("UK");
    public static readonly Country UnitedStates = new Country("US");
    public static readonly Country France = new Country("FR");
    public static readonly Country Protugal = new Country("PT");

    private Country(string shortName)
    {
        ShortName = shortName;
    }

    public string ShortName { get; private set; }
}

From this point you could add more properties, but beware of how much you add to the class, and how many static members you add, as the memory bloat it adds could make it not worth it.从这一点开始,您可以添加更多属性,但要注意您向 class 添加了多少,以及您添加了多少 static 成员,因为它添加的 memory 膨胀可能使它不值得。

I don't think there are many cases where this strategy is the best approach, but it is an option to be aware of when attempting to add properties or attributes to something you want to be able to treat as essentially an enum.我不认为在很多情况下这种策略是最好的方法,但在尝试将属性或特性添加到您希望能够将其视为本质上是枚举的对象时,这是一个需要注意的选项。

I had to leave my work on this project for a while, and having come back to it, I had a moment of inspiration.我不得不暂时离开这个项目的工作,回来后,我有了灵感。

Rather than an enum, I created a new class like so:我创建了一个新的 class 而不是枚举,如下所示:

public class Country
{
    public const string UnitedKingdom = "UK";
    public const string France = "F";
}

This way I can use Country.UnitedKingdom in my code and the value "UK" will be used.这样我就可以在我的代码中使用 Country.UnitedKingdom 并且将使用值“UK”。

I'm just posting this answer as an alternative solution.我只是将此答案作为替代解决方案发布。

Neil尼尔

Just use the DescriptionAttribute只需使用DescriptionAttribute

No need to create a dictionary if you only need to get a String representation for your enum values.如果您只需要为枚举值获取字符串表示形式,则无需创建字典。 See this example看这个例子

[EDIT] Oh... forgot to mention that it is more reusable than dictionaries, since you only need one common util class to help with getting the description and then all you need to do is add the DescriptionAttribute next time you add an enum value or you create a new enum with the same requirements. [编辑] 哦...忘了提它比字典更可重用,因为您只需要一个通用工具 class 来帮助获取描述,然后您需要做的就是在下次添加枚举值时添加 DescriptionAttribute或者您创建一个具有相同要求的新枚举。 In the dictionary/switch solution, it is harder to maintain and it gets messy once you have many enum types.在字典/开关解决方案中,它更难维护,一旦你有很多枚举类型,它就会变得混乱。

I tried to submit an edit to Scott Ivey's answer, but it was rejected, here's yet another answer.我试图提交对 Scott Ivey 的回答的修改,但被拒绝了,这是另一个答案。 My relatively minor edits:我相对较小的编辑:

1) I fixed Alex's error of System.ArgumentException: Field 'value__' defined on type 'MyClass.EnumHelperTest+MyCountryEnum' is not a field on the target object which is of type 'System.Reflection.RtFieldInfo'. 1) 我修复了 Alex 的System.ArgumentException: Field 'value__' defined on type 'MyClass.EnumHelperTest+MyCountryEnum' is not a field on the target object which is of type 'System.Reflection.RtFieldInfo'. Got it from here .这里得到它。

2) Added a return so you can actually copy/paste it and it'll work. 2)添加了一个return ,这样你就可以实际复制/粘贴它并且它会起作用。

3) Changed the SortedDictionary to Dictionary because SortedDictionary always sorts by the key, in this case the string Description. 3) 将 SortedDictionary 更改为 Dictionary,因为 SortedDictionary 始终按键排序,在本例中为字符串 Description。 There's no reason to alphabetize the Description.没有理由按字母顺序排列描述。 In fact, sorting it destroys the original order of the enum.事实上,对其进行排序会破坏枚举的原始顺序。 Dictionary doesn't preserve the enum order either, but at least it doesn't imply order like SortedDictionary does. Dictionary 也不保留枚举顺序,但至少它不像 SortedDictionary 那样暗示顺序。

enum MyCountryEnum
{    
    [Description("UK")]
    UnitedKingdom = 0,    

    [Description("US")]
    UnitedStates = 1,    

    [Description("FR")]
    France = 2,    

    [Description("PO")]
    Portugal = 3
}

public static string GetDescription(this Enum value)
{
    var type = value.GetType();

    var fi = type.GetField(value.ToString());

    var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];

    return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}

public static Dictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
    // validate.
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("T must be an Enum type.");
    }

    var results = new Dictionary<string, T>();

    FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);

    foreach (var fi in fieldInfos)
    {

        var value = (T)fi.GetValue(fi);
        var description = GetDescription((Enum)fi.GetValue(fi));

        if (!results.ContainsKey(description))
        {
            results.Add(description, value);
        }
    }
    return results;
}

Whenever I see an enum I feel that the code should be refactored.每当我看到枚举时,我都觉得应该重构代码。 Why not make a Country class and add methods to do some of the obstacles you are trying to get around.为什么不创建一个 Country class 并添加方法来完成您试图绕过的一些障碍。 Assigning values to an enum is an even bigger code smell.为枚举赋值是一种更大的代码味道。

Why the downvotes?为什么反对票? I think it is pretty widely accepted that using a polymorphic approach is better than using an enum.我认为使用多态方法比使用枚举更好这一观点已被广泛接受。 There is zero reason to use an enum when you can use the ValueObject design instead.当您可以改用 ValueObject 设计时,使用枚举的理由为零。

Here is a good blog post on the topic: http://devpinoy.org/blogs/cruizer/archive/2007/09/12/enums-are-evil.aspx这是关于该主题的一篇很好的博客文章: http://devpinoy.org/blogs/cruizer/archive/2007/09/12/enums-are-evil.aspx

var codes = new Dictionary<Country, string>() 
        { { Country.UnitedKingdom, "UK" },
        { Country.UnitedStates, "US" },
        { Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);

The following solution works (compiles and runs).以下解决方案有效(编译并运行)。 I see two issues:我看到两个问题:

  1. You would have to make sure the enums are in sync.您必须确保枚举是同步的。 (An automated test can do that for you.) (自动化测试可以为您做到这一点。)

  2. You would be relying in the fact that enums are not type safe in .NET.你会依赖于枚举在 .NET 中不是类型安全的事实。

     enum Country { UnitedKingdom = 0, UnitedStates = 1, France = 2, Portugal = 3 } enum CountryCode { UK = 0, US = 1, FR = 2, PT = 3 } void Main() { string countryCode = ((CountryCode)Country.UnitedKingdom).ToString(); Console.WriteLine(countryCode); countryCode = ((CountryCode)Country.Portugal).ToString(); Console.WriteLine(countryCode); }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM