我的枚举可以有友好的名字吗?

JL.*_*JL. 170 c# enums

我有以下内容 enum

public enum myEnum
{
    ThisNameWorks, 
    This Name doesn't work
    Neither.does.this;
}
Run Code Online (Sandbox Code Playgroud)

enum带有"友好名字"的s 不可能吗?

Tho*_*que 374

你可以使用该Description属性,如Yuriy建议的那样.以下扩展方法可以轻松获取枚举的给定值的描述:

public static string GetDescription(this Enum value)
{
    Type type = value.GetType();
    string name = Enum.GetName(type, value);
    if (name != null)
    {
        FieldInfo field = type.GetField(name);
        if (field != null)
        {
            DescriptionAttribute attr = 
                   Attribute.GetCustomAttribute(field, 
                     typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attr != null)
            {
                return attr.Description;
            }
        }
    }
    return null;
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

public enum MyEnum
{
    [Description("Description for Foo")]
    Foo,
    [Description("Description for Bar")]
    Bar
}

MyEnum x = MyEnum.Foo;
string description = x.GetDescription();
Run Code Online (Sandbox Code Playgroud)

  • 当帖子不包含它们引用的非默认命名空间时,这很恼人......`System.ComponentModel`和`System.Reflection` (52认同)
  • @musefan,我从不包括它们,因为它们只会增加噪音.只需单击2次,Visual Studio就可以自动添加它们... (42认同)
  • 我打算今晚加入对UnconstrainedMelody的支持. (9认同)
  • @ThomasLevesque经常会有多个名称空间匹配(尤其是类似描述的泛型类).也不是每个人都使用Visual Studio来编写c# (7认同)
  • 这应该被接受作为答案,而不是目前接受的答案. (4认同)

RaY*_*ell 78

枚举值名称必须遵循与C#中所有标识符相同的命名规则,因此只有名字是正确的.

  • 您可以使用`System.ComponentModel.DataAnnotations`并添加一个显示attr.(例如:`[显示(名称="此名称不起作用")]`) (47认同)
  • Cas 的评论实际上是目前最常用的解决方案。 (2认同)

Mar*_*ter 33

如果您有以下枚举:

public enum MyEnum {
    First,
    Second,
    Third
}
Run Code Online (Sandbox Code Playgroud)

您可以声明扩展方法MyEnum(就像任何其他类型一样).我只是掀起了这个:

namespace Extension {
    public static class ExtensionMethods {
        public static string EnumValue(this MyEnum e) {
            switch (e) {
                case MyEnum.First:
                    return "First Friendly Value";
                case MyEnum.Second:
                    return "Second Friendly Value";
                case MyEnum.Third:
                    return "Third Friendly Value";
            }
            return "Horrible Failure!!";
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此扩展方法,以下内容现在是合法的:

Console.WriteLine(MyEnum.First.EnumValue());
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!!

  • @Roy - 这并不总是正确的,有时(正是基于实现细节的时候)C#编译器将发出字典查找,请参阅:http://stackoverflow.com/questions/3366376/are-net-switch-statements -hashed-或索引/ 3366497#3366497 (12认同)
  • 罗伊,我在这里错了,但我的印象是这不是真的.虽然C/C++编译器可能会发出if/else if链,但我认为如果编译器认为合适,它也可以发出跳转表.例如,如果你关闭一个字节值,我认为它很可能只是发出一个跳转表. (2认同)

Yur*_*ich 23

不,但您可以使用DescriptionAttribute来完成您正在寻找的内容.


MRG*_*MRG 14

您可以使用该Description属性获取该友好名称.您可以使用以下代码:

public static string ToStringEnums(Enum en)
{
    Type type = en.GetType();

    MemberInfo[] memInfo = type.GetMember(en.ToString());
    if (memInfo != null && memInfo.Length > 0)
    {
        object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attrs != null && attrs.Length > 0)
            return ((DescriptionAttribute)attrs[0]).Description;
    }
    return en.ToString();
}
Run Code Online (Sandbox Code Playgroud)

您希望使用此方法的示例:当您的枚举值为EncryptionProviderType并且您想要enumVar.Tostring()返回"加密提供程序类型"时.

先决条件:应使用属性应用所有枚举成员[Description("String to be returned by Tostring()")].

枚举示例:

enum ExampleEnum
{
    [Description("One is one")]
    ValueOne = 1,
    [Description("Two is two")]
    ValueTow = 2
}
Run Code Online (Sandbox Code Playgroud)

在你的课堂上,你会像这样使用它:

ExampleEnum enumVar = ExampleEnum.ValueOne;
Console.WriteLine(ToStringEnums(enumVar));
Run Code Online (Sandbox Code Playgroud)


Tra*_*ife 10

这个技巧的一个问题是描述属性不能被本地化.我喜欢Sacha Barber的一种技术,他在那里创建了自己的Description属性版本,该属性将从相应的资源管理器中获取值.

http://www.codeproject.com/KB/WPF/FriendlyEnums.aspx

虽然本文是围绕WPF开发人员绑定到枚举时通常遇到的问题,但您可以直接跳转到他创建LocalizableDescriptionAttribute的部分.


And*_*ney 8

已经发布了一些很棒的解决方案.当我遇到这个问题时,我想要双向:将枚举转换为描述,并将匹配描述的字符串转换为枚举.

我有两个变种,慢和快.两者都从枚举转换为字符串,字符串转换为枚举.我的问题是我有这样的枚举,其中一些元素需要属性,有些则不需要.我不想将属性放在不需要它们的元素上.我目前总共有大约一百个:

public enum POS
{   
    CC, //  Coordinating conjunction
    CD, //  Cardinal Number
    DT, //  Determiner
    EX, //  Existential there
    FW, //  Foreign Word
    IN, //  Preposision or subordinating conjunction
    JJ, //  Adjective
    [System.ComponentModel.Description("WP$")]
    WPDollar, //$   Possessive wh-pronoun
    WRB, //     Wh-adverb
    [System.ComponentModel.Description("#")]
    Hash,
    [System.ComponentModel.Description("$")]
    Dollar,
    [System.ComponentModel.Description("''")]
    DoubleTick,
    [System.ComponentModel.Description("(")]
    LeftParenth,
    [System.ComponentModel.Description(")")]
    RightParenth,
    [System.ComponentModel.Description(",")]
    Comma,
    [System.ComponentModel.Description(".")]
    Period,
    [System.ComponentModel.Description(":")]
    Colon,
    [System.ComponentModel.Description("``")]
    DoubleBackTick,
    };
Run Code Online (Sandbox Code Playgroud)

解决这个问题的第一种方法很慢,并且基于我在这里和网络上看到的建议.它很慢,因为我们反映每次转换:

using System;
using System.Collections.Generic;
namespace CustomExtensions
{

/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
public static class EnumExtensions
{
    /// <summary>
    /// Gets the description string, if available. Otherwise returns the name of the enum field
    /// LthWrapper.POS.Dollar.GetString() yields "$", an impossible control character for enums
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string GetStringSlow(this Enum value)
    {
        Type type = value.GetType();
        string name = Enum.GetName(type, value);
        if (name != null)
        {
            System.Reflection.FieldInfo field = type.GetField(name);
            if (field != null)
            {
                System.ComponentModel.DescriptionAttribute attr =
                       Attribute.GetCustomAttribute(field,
                         typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;
                if (attr != null)
                {
                    //return the description if we have it
                    name = attr.Description; 
                }
            }
        }
        return name;
    }

    /// <summary>
    /// Converts a string to an enum field using the string first; if that fails, tries to find a description
    /// attribute that matches. 
    /// "$".ToEnum<LthWrapper.POS>() yields POS.Dollar
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T ToEnumSlow<T>(this string value) //, T defaultValue)
    {
        T theEnum = default(T);

        Type enumType = typeof(T);

        //check and see if the value is a non attribute value
        try
        {
            theEnum = (T)Enum.Parse(enumType, value);
        }
        catch (System.ArgumentException e)
        {
            bool found = false;
            foreach (T enumValue in Enum.GetValues(enumType))
            {
                System.Reflection.FieldInfo field = enumType.GetField(enumValue.ToString());

                System.ComponentModel.DescriptionAttribute attr =
                           Attribute.GetCustomAttribute(field,
                             typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;

                if (attr != null && attr.Description.Equals(value))
                {
                    theEnum = enumValue;
                    found = true;
                    break;

                }
            }
            if( !found )
                throw new ArgumentException("Cannot convert " + value + " to " + enumType.ToString());
        }

        return theEnum;
    }
}
}
Run Code Online (Sandbox Code Playgroud)

这个问题是你每次都在做反思.我没有测量过这样做的性能,但似乎令人担忧.更糟糕的是,我们反复计算这些昂贵的转换,而不是缓存它们.

相反,我们可以使用静态构造函数来填充一些带有此转换信息的字典,然后在需要时查找此信息.显然静态类(扩展方法所需)可以有构造函数和字段:)

using System;
using System.Collections.Generic;
namespace CustomExtensions
{

/// <summary>
/// uses extension methods to convert enums with hypens in their names to underscore and other variants
/// I'm not sure this is a good idea. While it makes that section of the code much much nicer to maintain, it 
/// also incurs a performance hit via reflection. To circumvent this, I've added a dictionary so all the lookup can be done once at 
/// load time. It requires that all enums involved in this extension are in this assembly.
/// </summary>
public static class EnumExtensions
{
    //To avoid collisions, every Enum type has its own hash table
    private static readonly Dictionary<Type, Dictionary<object,string>> enumToStringDictionary = new Dictionary<Type,Dictionary<object,string>>();
    private static readonly Dictionary<Type, Dictionary<string, object>> stringToEnumDictionary = new Dictionary<Type, Dictionary<string, object>>();

    static EnumExtensions()
    {
        //let's collect the enums we care about
        List<Type> enumTypeList = new List<Type>();

        //probe this assembly for all enums
        System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
        Type[] exportedTypes = assembly.GetExportedTypes();

        foreach (Type type in exportedTypes)
        {
            if (type.IsEnum)
                enumTypeList.Add(type);
        }

        //for each enum in our list, populate the appropriate dictionaries
        foreach (Type type in enumTypeList)
        {
            //add dictionaries for this type
            EnumExtensions.enumToStringDictionary.Add(type, new Dictionary<object,string>() );
            EnumExtensions.stringToEnumDictionary.Add(type, new Dictionary<string,object>() );

            Array values = Enum.GetValues(type);

            //its ok to manipulate 'value' as object, since when we convert we're given the type to cast to
            foreach (object value in values)
            {
                System.Reflection.FieldInfo fieldInfo = type.GetField(value.ToString());

                //check for an attribute 
                System.ComponentModel.DescriptionAttribute attribute =
                       Attribute.GetCustomAttribute(fieldInfo,
                         typeof(System.ComponentModel.DescriptionAttribute)) as System.ComponentModel.DescriptionAttribute;

                //populate our dictionaries
                if (attribute != null)
                {
                    EnumExtensions.enumToStringDictionary[type].Add(value, attribute.Description);
                    EnumExtensions.stringToEnumDictionary[type].Add(attribute.Description, value);
                }
                else
                {
                    EnumExtensions.enumToStringDictionary[type].Add(value, value.ToString());
                    EnumExtensions.stringToEnumDictionary[type].Add(value.ToString(), value);
                }
            }
        }
    }

    public static string GetString(this Enum value)
    {
        Type type = value.GetType();
        string aString = EnumExtensions.enumToStringDictionary[type][value];
        return aString; 
    }

    public static T ToEnum<T>(this string value)
    {
        Type type = typeof(T);
        T theEnum = (T)EnumExtensions.stringToEnumDictionary[type][value];
        return theEnum;
    }
 }
}
Run Code Online (Sandbox Code Playgroud)

看看转换方法现在有多紧张.我能想到的唯一缺陷是,这需要所有转换后的枚举都在当前程序集中.此外,我只打扰导出的枚举,但如果你愿意,你可以改变它.

这是如何调用方法

 string x = LthWrapper.POS.Dollar.GetString();
 LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();
Run Code Online (Sandbox Code Playgroud)