代码之家  ›  专栏  ›  技术社区  ›  JL. Hans Passant

我的枚举能有友好的名称吗?[复制品]

  •  160
  • JL. Hans Passant  · 技术社区  · 15 年前

    这个问题已经有了答案:

    我有以下内容 enum

    public enum myEnum
    {
        ThisNameWorks, 
        This Name doesn't work
        Neither.does.this;
    }
    

    不可能有 枚举 有“友好的名字”吗?

    13 回复  |  直到 6 年前
        1
  •  74
  •   RaYell    10 年前

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

        2
  •  355
  •   Lauren Van Sloun Sebastian Inones    6 年前

    你可以用 Description 属性,正如尤里建议的。以下扩展方法使获取枚举的给定值的描述变得容易:

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

    您可以这样使用它:

    public enum MyEnum
    {
        [Description("Description for Foo")]
        Foo,
        [Description("Description for Bar")]
        Bar
    }
    
    MyEnum x = MyEnum.Foo;
    string description = x.GetDescription();
    
        3
  •  33
  •   Mark Carpenter    15 年前

    如果具有以下枚举:

    public enum MyEnum {
        First,
        Second,
        Third
    }
    

    您可以为声明扩展方法 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!!";
            }
        }
    }
    

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

    Console.WriteLine(MyEnum.First.EnumValue());
    

    希望这有帮助!!

        4
  •  23
  •   Yuriy Faktorovich    15 年前

    不,但是你可以用 DescriptionAttribute 完成你想要的。

        5
  •  12
  •   Lauren Van Sloun Sebastian Inones    6 年前

    你可以使用 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();
    }
    

    当枚举值为 EncryptionProviderType 你想要 enumVar.Tostring() 返回“加密提供程序类型”。

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

    实例枚举:

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

    在你的课堂上,你可以这样使用它:

    ExampleEnum enumVar = ExampleEnum.ValueOne;
    Console.WriteLine(ToStringEnums(enumVar));
    
        6
  •  9
  •   Trainee4Life    15 年前

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

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

    尽管本文讨论了WPF开发人员在绑定到枚举时通常会遇到的问题,但是您可以直接跳到他创建本地化描述属性的部分。

        7
  •  6
  •   Andrew Olney    14 年前

    一些伟大的解决方案已经发布。当我遇到这个问题时,我想双向进行:将枚举转换为描述,并将与描述匹配的字符串转换为枚举。

    我有两种变体,慢的和快的。两者都从枚举转换为字符串,从字符串转换为枚举。我的问题是我有这样的枚举,有些元素需要属性,有些不需要。我不想在不需要属性的元素上放置属性。我目前有大约一百个这样的总数:

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

    处理这个问题的第一个方法是缓慢的,基于我在这里和网络周围看到的建议。它很慢,因为我们对每一个转换都进行反射:

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

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

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

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

    看看转换方法现在有多紧。我能想到的唯一缺陷是,这要求所有转换后的枚举都在当前程序集中。另外,我只关心导出的枚举,但是如果您愿意,您可以更改它。

    这是如何调用方法

     string x = LthWrapper.POS.Dollar.GetString();
     LthWrapper.POS y = "PRP$".ToEnum<LthWrapper.POS>();
    
        8
  •  4
  •   aJ.    15 年前
    public enum myEnum
    {
             ThisNameWorks, 
             This_Name_can_be_used_instead,
    
    }
    
        9
  •  4
  •   Mark Menchavez    13 年前

    在阅读了许多关于这个主题的资源(包括stackoverflow)之后,我发现并不是所有的解决方案都能正常工作。下面是我们试图解决的问题。

    基本上,如果枚举存在,我们从DescriptionAttribute中获取它的友好名称。
    如果没有,我们使用regex来确定枚举名称中的单词并添加空格。

    下一个版本,我们将使用另一个属性来标记是否可以/应该从可本地化的资源文件中获取友好名称。

    下面是测试用例。如果您还有其他未通过的测试用例,请报告。

    public static class EnumHelper
    {
        public static string ToDescription(Enum value)
        {
            if (value == null)
            {
                return string.Empty;
            }
    
            if (!Enum.IsDefined(value.GetType(), value))
            {
                return string.Empty;
            }
    
            FieldInfo fieldInfo = value.GetType().GetField(value.ToString());
            if (fieldInfo != null)
            {
                DescriptionAttribute[] attributes =
                    fieldInfo.GetCustomAttributes(typeof (DescriptionAttribute), false) as DescriptionAttribute[];
                if (attributes != null && attributes.Length > 0)
                {
                    return attributes[0].Description;
                }
            }
    
            return StringHelper.ToFriendlyName(value.ToString());
        }
    }
    
    public static class StringHelper
    {
        public static bool IsNullOrWhiteSpace(string value)
        {
            return value == null || string.IsNullOrEmpty(value.Trim());
        }
    
        public static string ToFriendlyName(string value)
        {
            if (value == null) return string.Empty;
            if (value.Trim().Length == 0) return string.Empty;
    
            string result = value;
    
            result = string.Concat(result.Substring(0, 1).ToUpperInvariant(), result.Substring(1, result.Length - 1));
    
            const string pattern = @"([A-Z]+(?![a-z])|\d+|[A-Z][a-z]+|(?![A-Z])[a-z]+)+";
    
            List<string> words = new List<string>();
            Match match = Regex.Match(result, pattern);
            if (match.Success)
            {
                Group group = match.Groups[1];
                foreach (Capture capture in group.Captures)
                {
                    words.Add(capture.Value);
                }
            }
    
            return string.Join(" ", words.ToArray());
        }
    }
    
    
        [TestMethod]
        public void TestFriendlyName()
        {
            string[][] cases =
                {
                    new string[] {null, string.Empty},
                    new string[] {string.Empty, string.Empty},
                    new string[] {" ", string.Empty}, 
                    new string[] {"A", "A"},
                    new string[] {"z", "Z"},
    
                    new string[] {"Pascal", "Pascal"},
                    new string[] {"camel", "Camel"},
    
                    new string[] {"PascalCase", "Pascal Case"}, 
                    new string[] {"ABCPascal", "ABC Pascal"}, 
                    new string[] {"PascalABC", "Pascal ABC"}, 
                    new string[] {"Pascal123", "Pascal 123"}, 
                    new string[] {"Pascal123ABC", "Pascal 123 ABC"}, 
                    new string[] {"PascalABC123", "Pascal ABC 123"}, 
                    new string[] {"123Pascal", "123 Pascal"}, 
                    new string[] {"123ABCPascal", "123 ABC Pascal"}, 
                    new string[] {"ABC123Pascal", "ABC 123 Pascal"}, 
    
                    new string[] {"camelCase", "Camel Case"}, 
                    new string[] {"camelABC", "Camel ABC"}, 
                    new string[] {"camel123", "Camel 123"}, 
                };
    
            foreach (string[] givens in cases)
            {
                string input = givens[0];
                string expected = givens[1];
                string output = StringHelper.ToFriendlyName(input);
    
                Assert.AreEqual(expected, output);
            }
        }
    }
    
        10
  •  3
  •   TimothyP    15 年前

    它们遵循与变量名相同的命名规则。 因此,它们不应该包含空格。

    另外,你的建议无论如何都是非常糟糕的做法。

        11
  •  2
  •   Robban    15 年前

    枚举名与普通变量名的规则相同,即名称中间没有空格或圆点…不过我还是认为第一个相当友好的…

        12
  •  0
  •   Jamie    13 年前

    这是一个糟糕的主意,但确实有效。

        public enum myEnum
    {
        ThisNameWorks,
        ThisNameDoesntWork149141331,// This Name doesn't work
        NeitherDoesThis1849204824// Neither.does.this;
    }
    
    class Program
    {
        private static unsafe void ChangeString(string original, string replacement)
        {
            if (original.Length < replacement.Length)
                throw new ArgumentException();
    
            fixed (char* pDst = original)
            fixed (char* pSrc = replacement)
            {
                // Update the length of the original string
                int* lenPtr = (int*)pDst;
                lenPtr[-1] = replacement.Length;
    
                // Copy the characters
                for (int i = 0; i < replacement.Length; i++)
                    pDst[i] = pSrc[i];
            }
        }
    
        public static unsafe void Initialize()
        {
            ChangeString(myEnum.ThisNameDoesntWork149141331.ToString(), "This Name doesn't work");
            ChangeString(myEnum.NeitherDoesThis1849204824.ToString(), "Neither.does.this");
        }
    
        static void Main(string[] args)
        {
            Console.WriteLine(myEnum.ThisNameWorks);
            Console.WriteLine(myEnum.ThisNameDoesntWork149141331);
            Console.WriteLine(myEnum.NeitherDoesThis1849204824);
    
            Initialize();
    
            Console.WriteLine(myEnum.ThisNameWorks);
            Console.WriteLine(myEnum.ThisNameDoesntWork149141331);
            Console.WriteLine(myEnum.NeitherDoesThis1849204824);
        }
    

    要求

    1. 枚举名称的字符数必须等于或大于所需的字符串数。

    2. 你的枚举名不应该在任何地方重复,只是为了防止字符串插入把事情搞砸。

    为什么这是个坏主意(几个原因)

    1. 您的枚举名称由于需求而变得丑陋

    2. 它需要您尽早调用初始化方法。

    3. 不安全指针

    4. 如果字符串的内部格式发生更改,例如,如果长度字段被移动,则说明

    5. 如果Enum.ToString()曾经更改过,使它只返回一个副本,那么您就完蛋了

    6. Raymond Chen会抱怨您使用了未记录的功能,以及CLR团队无法在下一周的.NET工作中优化将运行时间缩短50%是您的错。

        13
  •  -2
  •   Lauren Van Sloun Sebastian Inones    6 年前

    我认为您希望向用户显示枚举值,因此,您希望它们具有一些友好的名称。

    我的建议是:

    使用枚举类型模式。虽然它需要一些努力来实现,但它确实是值得的。

    public class MyEnum
    {  
        public static readonly MyEnum Enum1=new MyEnum("This will work",1);
        public static readonly MyEnum Enum2=new MyEnum("This.will.work.either",2);
        public static readonly MyEnum[] All=new []{Enum1,Enum2};
        private MyEnum(string name,int value)
        {
            Name=name;
            Value=value;
        }
    
        public string Name{get;set;}
        public int Value{get;set;}
    
        public override string ToString()
        {
            return Name;    
        }
    }