1. 程式人生 > >寫爛了的C#獲取列舉對應的描述資訊

寫爛了的C#獲取列舉對應的描述資訊

為什麼說是寫爛了的?這的確是寫爛了的,N年就寫過,網上一搜更是一大把

既然是寫爛了的,為啥還要再寫?首先因為網上都是僅僅反射獲取,沒考慮過額外的增加一個快取步驟來提高效能;其次網上的也都只是對應特定特性,如果需要再支援其他特性就需要改下程式碼;最後如果是第三方列舉,本身並沒提供任何描述特性,這裡也可以通過一個額外的註冊過程來達到與有描述特性一致的使用效果

因為僅是輔助類,所以無需長篇累牘,直接上程式碼

    using System.Collections.Concurrent;
    /// <summary>
    /// 列舉輔助類
    /// </summary>
    public static class EnumHelper
    {
        private static readonly ConcurrentDictionary<Type, Dictionary<long,string>> descDictionary
            = new ConcurrentDictionary<Type, Dictionary<long, string>>();
        /// <summary>
        /// 獲取列舉對應的描述,該方法僅簡單的獲取描述資訊,如未註冊過描述資訊則直接返回ToString()結果
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static string GetDescription(this Enum input)
        {
            var enumType = input.GetType();
            if (enumType.IsEnum && descDictionary.TryGetValue(enumType, out Dictionary<long, string> dic) && dic != null)
            {
                var key = Convert.ToInt64(input);
                if (dic.ContainsKey(key))
                {
                    return dic[key];
                }
            }
            return input?.ToString();
        }
        /// <summary>
        /// 獲取列舉對應的描述,該方法會在當前TEnum尚未註冊描述資訊時自動註冊描述資訊
        /// </summary>
        /// <typeparam name="TArrtibute">用於獲取描述的Attribute</typeparam>
        /// <param name="input">列舉值</param>
        /// <param name="attrPropName">Attribute中用於獲取描述值的public屬性</param>
        /// <returns></returns>
        public static string GetDescription<TArrtibute>(this Enum input, string attrPropName = "Description")
            where TArrtibute : Attribute
        {
            var enumType = input.GetType();
            var attrType = typeof(TArrtibute);
            RegisterDescription(enumType, attrType, out Dictionary<long, string> dic, attrPropName);
            var key = Convert.ToInt64(input);
            if (dic != null && dic.Count > 0 && dic.ContainsKey(key))
            {
                return dic[key];
            }
            return input?.ToString();
        }
        /// <summary>
        /// 註冊列舉的描述,假如已註冊過屬性描述資訊,則該方法將不進行任何處理
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <typeparam name="TArrtibute">用於獲取描述的Attribute</typeparam>
        /// <param name="attrPropName">Attribute中用於獲取描述值的public屬性</param>
        public static void RegisterDescription<TEnum, TArrtibute>(string attrPropName = "Description")
            where TArrtibute : Attribute
        {
            var enumType = typeof(TEnum);
            var attrType = typeof(TArrtibute);
            RegisterDescription(enumType, attrType, out Dictionary<long, string> dictionary, attrPropName);
        }
        /// <summary>
        /// 註冊列舉的描述,注意此處的註冊會替換已有的描述列舉(假如有)
        /// </summary>
        /// <typeparam name="TEnum"></typeparam>
        /// <param name="dictionary">包含描述的字典</param>
        public static void RegisterDescription<TEnum>(IDictionary<TEnum, string> dictionary)
        {
            var enumType = typeof(TEnum);
            if (enumType.IsEnum)
            {
                var dic = dictionary.ToDictionary(k => Convert.ToInt64(k.Key), v => v.Value);
                descDictionary.AddOrUpdate(enumType, dic, (k, v) => dic);
            }
        }
        private static void RegisterDescription(Type enumType, Type attrType, out Dictionary<long, string> dictionary, string attrPropName)
        {
            dictionary = null;
            //僅列舉時才進行註冊
            if (enumType.IsEnum
                && !descDictionary.TryGetValue(enumType, out dictionary))
            {
                var arrs = Enum.GetValues(enumType);
                dictionary = new Dictionary<long, string>();
                foreach (var e in arrs)
                {
                    var desc = e.ToString();
                    var attrs = enumType.GetField(desc).GetCustomAttributes(attrType, false);
                    if (attrs.Length > 0)
                    {
                        var prop = attrs[0].GetType().GetProperty(attrPropName);
                        if (prop != null)
                        {
                            desc = prop.GetValue(attrs[0]).ToString();
                        }
                    }
                    dictionary.Add(Convert.ToInt64(e), desc);
                }
                descDictionary.TryAdd(enumType, dictionary);
            }
        }
    }
使用例子如下,首先是兩個自定義列舉
        enum SecondDemoEnum
        {
            [DebuggerDisplay("DebuggerDisplay.X")]
            X = 9,
            [DebuggerDisplay("DebuggerDisplay.Y")]
            Y = 12,
            Z = 18
        }

        enum FirstDemoEnum : byte
        {
            [Description("Description[A]")]
            A,
            [Description("Description[B]")]
            B,
            C
        }
然後是具體的例子程式碼
            Console.WriteLine("列舉 {0} 對應描述 {1}",DateTimeKind.Local, DateTimeKind.Local.GetDescription());
            //對於沒有設定Attribute的列舉,可以通過以下方式註冊描述值
            var dic = new Dictionary<DateTimeKind, string> { { DateTimeKind.Local, "本地時間" } };
            //注意此處註冊字典時不能直接註冊為 Dictionary<Enum, string> 
            EnumHelper.RegisterDescription(dic);
            
            Console.WriteLine("列舉 {0} 對應描述 {1}", DateTimeKind.Local, DateTimeKind.Local.GetDescription());

            //描述屬性為DescriptionAttribute的demo
            foreach (var demoEnum in Enum.GetValues(typeof(FirstDemoEnum)))
            {
                FirstDemoEnum input = (FirstDemoEnum)demoEnum;
                Console.WriteLine("列舉 {0} 對應描述 {1}", input, input.GetDescription<DescriptionAttribute>());
            }
            //描述屬性為DebuggerDisplayAttribute的demo
            foreach (var demoEnum in Enum.GetValues(typeof(SecondDemoEnum)))
            {
                SecondDemoEnum input = (SecondDemoEnum)demoEnum;
                //因為DebuggerDisplayAttribute預設屬性對應Value,所以此處需傳入屬性名
                Console.WriteLine("列舉 {0} 對應描述 {1}", input, input.GetDescription<DebuggerDisplayAttribute>("Value"));
            }
執行結果如下