天天看點

為枚舉值添加自定義描述

公告:本文有最新更新檔

在很多時候,我們需要定義和擷取枚舉值,然而感覺到不夠爽的就是當我們需要向使用者描述一個枚舉值的時候非常的不友善。通常我們有以下幾個解決方案:

  • 使用 if 語句;
  • 使用 switch 語句;
  • 使用數組索引;
  • 使用字典索引;
  • ……

然而這些方案在實踐過程中并非都一帆風順,在項目較小時,使用以上的方案是明智的,也非常的簡單、友善。可是在大型項目當中,當别人看到你的一大堆的 if-else、switch等語句的時候——絕對頭暈!

這個時候,使用字典索引來解決這個問題還是非常好的,尤其是泛型字典。但問題是,它雖然可以通用、實作原理也很簡單,但是如果在代碼中定義一堆一堆的關于枚舉值的自定義描述字典,總是讓人感覺到不爽。

有人說了:“可以用資料庫嘛!”。是的,可以使用資料庫,但是為了開發一個輕量級項目定義這麼個資料庫有些浪費。

又有人說了:“可以利用 Attribute 特性!”。這個方案可行,但是無法支援 .Net Framework 或第三方代碼中已經定義好的枚舉值的描述。

還有人說了:“既然如此,兩者結合就可以解決這個問題!”。好!事實上 YMind 的構思就是通過結合兩種方案的優點來做的。

YMind 整合了很多先進的程式設計思想開發了幾個專門解決這個問題的類,使用它們就可以完美的解決以上所有的問題。

在封裝枚舉值自定義描述的操作的時候,YMind查閱了很多的資料,也看到了很多朋友實作的優秀的方案,但總結起來還是有一些缺點的,例如:

  1. 有些方案不支援多語言;
  2. 有些方案不支援位域操作;
  3. 有些方案性能太差;
  4. 幾乎所有的方案都不支援“第三方已定義的枚舉值”;

問題總是有辦法解決的,多語言、位域操作可以整合這些先人們的成就即可解決,支援“第三方已定義的枚舉值”可以采用字典解決,而性能優化可以對代碼進行重構。說起來容易,可做起來難!下面介紹一下具體實作。

一、使用 Attribute 特性:

要使用 Attribute 特性,就需要先實作一個自己的 Attribute 類,這個類就是作為枚舉值自定義描述的,它可以支援無限的繼承,這就意味着,開發者可以根據自己的情況為不同的枚舉值定義更多更豐富的功能。

EnumDescriptionAttribute類的代碼如下:

  1. // ===============================================================================  
  2. //  産品名稱:網鳥小刺客便捷工具箱  
  3. //  産品作者:YMind Chan  
  4. //  版權所有:網鳥IT技術論壇 顔銘工作室  
  5. // ===============================================================================  
  6. //  Copyright © Htmlbird.Net. All rights reserved .  
  7. //  官方網站:http://www.htmlbird.net/  
  8. //  技術論壇:http://bbs.htmlbird.net/  
  9. // ===============================================================================  
  10. using System;  
  11. namespace EnumDescriptionVictor.Modules  
  12. {  
  13.     /// <summary>  
  14.     /// 指定枚舉值的自定義描述資訊。  
  15.     /// </summary>  
  16.     [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]  
  17.     public class EnumDescriptionAttribute : Attribute  
  18.     {  
  19.         #region 構造函數  
  20.         /// <summary>  
  21.         /// 根據指定的預設描述資訊初始化 <see cref="EnumDescriptionAttribute"/> 類的新執行個體。  
  22.         /// </summary>  
  23.         /// <param name="text">指定包含該枚舉值的自定義描述資訊的字元串。</param>  
  24.         public EnumDescriptionAttribute(string text)  
  25.         {  
  26.             if (text == null) throw new ArgumentNullException("text");  
  27.             if (text.Length == 0) throw new ArgumentOutOfRangeException("text");  
  28.             this.Text = text;  
  29.         }  
  30.         #endregion  
  31.         #region 公有屬性  
  32.         /// <summary>  
  33.         /// 擷取或設定預設描述資訊。  
  34.         /// </summary>  
  35.         public string Text { get; set; }  
  36.         /// <summary>  
  37.         /// 擷取或設定使用者自定義選項。  
  38.         /// </summary>  
  39.         public object[] UserOptions { get; set; }  
  40.         #endregion  
  41.     }  

這個類很簡單,就不需要解釋什麼了,如果看不懂,請面壁三分鐘檢討一下!

二、如何使用我們定義好的 EnumDescriptionAttribute?

假設有一個枚舉值定義如下:

  1. /// <summary>  
  2. /// 表示性别的枚舉值。  
  3. /// </summary>  
  4. public enum GenderPreference  
  5. {  
  6.     /// <summary>  
  7.     /// 未知或未指定。  
  8.     /// </summary>  
  9.     Unknown = 0,  
  10.     /// <summary>  
  11.     /// 男性。  
  12.     /// </summary>  
  13.     Males = 1,  
  14.     /// <summary>  
  15.     /// 女性。  
  16.     /// </summary>  
  17.     Females = 2,  

使用我們定義好的 EnumDescriptionAttribute:

  1. /// <summary>  
  2. /// 表示性别的枚舉值。  
  3. /// </summary>  
  4. public enum GenderPreference  
  5. {  
  6.     /// <summary>  
  7.     /// 未知或未指定。  
  8.     /// </summary>  
  9.     [EnumDescription("未知")]  
  10.     Unknown = 0,  
  11.     /// <summary>  
  12.     /// 男性。  
  13.     /// </summary>  
  14.     [EnumDescription("男")]  
  15.     Males = 1,  
  16.     /// <summary>  
  17.     /// 女性。  
  18.     /// </summary>  
  19.     [EnumDescription("女")]  
  20.     Females = 2,  

看到了吧,很簡單!

其實隻要你了解什麼是Attribute,就早已明白如何使用了!

三、如何擷取為枚舉值定義好的EnumDescriptionAttribute呢?

這個問題說起來比較複雜,而且是本篇文章的核心所在。因為實作的代碼很多,是以封裝成了一個類:EnumVictor<T>。這個類的封裝,解決了我們一開始提到的所有的問題:

  1. 支援多語言;
  2. 支援位域操作;
  3. 支援“第三方已定義的枚舉值”;
  4. 大大的優化了性能;

擷取EnumDescriptionAttribute的方法的代碼如下(摘自:EnumVictor<T>):

  1. private void _Initialize()  
  2. {  
  3.     Array enumValues = Enum.GetValues(this.EnumType);  
  4.     foreach (Enum value in enumValues)  
  5.     {  
  6.         if (value is T == false) continue;  
  7.         T itemValue = (T)((object)value);  
  8.         if (this.DescriptionFilter != null && this.DescriptionFilter.Count > 0 && this.DescriptionFilter.ContainsKey(itemValue))  
  9.         {  
  10.             EnumDescriptionAttribute descAttribute = this._GetDescription(value.ToString());  
  11.             descAttribute.Text = this.DescriptionFilter[itemValue];  
  12.             this.Descriptions.Add(itemValue, descAttribute);  
  13.         }  
  14.         this.DefaultDescriptions.Add(itemValue, this._GetDescription(value.ToString()));  
  15.     }  

四、如何使用EnumVictor<T>?

首先我們需要初始化EnumVictor<T>,有以下兩種方式:

方式一:為已添加了 EnumDescriptionAttribute 标記的枚舉值初始化:

  1. EnumVictor<AnimalSign> AnimalSignEnumVictor = new EnumVictor<AnimalSign>(); 

方式二:為沒有或無法添加 EnumDescriptionAttribute 标記的枚舉值初始化(支援“第三方已定義的枚舉值”):

  1. Dictionary<AnimalSign, string> AnimalSignFilter = new Dictionary<AnimalSign, string>();  
  2. AnimalSignFilter.Add(AnimalSign.Boar, "豬豬");  
  3. // ....  
  4. EnumVictor<AnimalSign> AnimalSignEnumVictor = new EnumVictor<AnimalSign>(AnimalSignFilter); 

擷取自定義描述:

  1. AnimalSignEnumVictor.GetDescriptionValue(AnimalSign.Boar);  
  2. // 傳回:豬豬 

以位域操作的形式擷取枚舉值的自定義描述:

  1. Global.UserRolesEnumVictor.GetBitFieldDescriptionValue(UserRoles.Test);  
  2. // 指定值:Test  
  3. // 位域值:DataCensor, DataEntry, LogonLock  
  4. // 傳回值:[資料審查員],[資料錄入員],[禁止登入] 

怎麼樣?很爽吧!

轉載網址:

http://www.ymind.net/Article/73.aspx