DescriptionAttribute特性可以用到很多地方,比較常見的就是枚舉,通過擷取枚舉上定義的描述資訊在UI上顯示,一個簡單的枚舉定義:
本文不讨論DescriptionAttribute的其他應用場景,也不關注多語言的實作,隻單純的研究下擷取枚舉描述資訊的方法。
一般比較常見的擷取枚舉描述資訊的方法如下,可以在園子裡搜尋類似的代碼非常多。
簡單測試下:
女
男
Other
首先要了解特性是什麼?
特性: Attribute特性就是關聯了一個目标對象的一段配置資訊,存儲在dll内的中繼資料。它本身沒什麼意義,可以通過反射來擷取配置的特性資訊。
是以主要問題其實就是反射造成的嚴重性能問題:
1.每次調用都會使用反射,效率慢!
2.每次調用反射都會生成新的DescriptionAttribute對象,哪怕是同一個枚舉值。造成記憶體、GC的極大浪費!
3.好像不支援位域組合對象!
4.這個地方的方法參數是Enum,Enum是枚舉的基類,他是一個引用類型,而枚舉是值類型,該方法會造成裝箱,不過這個問題好像是不可避免的。
性能到底有多差呢?代碼來實測一下:
其中this.GetTestEnums();方法使用擷取一個枚舉值集合,用于測試的,集合大小80,執行100w次,相當于執行了8000w次GetDescriptionOriginal方法。
TestHelper.InvokeAndWriteAll方法是用來計算執行前後的時間、記憶體消耗、0代GC回收次數的,文末附錄中給出了代碼,由于記憶體回收的原因,記憶體消耗計算其實不準确的,不過可以參考第三個名額0代GC回收次數。
知道了問題原因,解決就好辦了,基本思路就是把擷取到的文本值緩存起來,一個枚舉值隻反射一次,這樣性能問題就解決了。
因為使用靜态變量字典來緩存值,就涉及到線程安全,需要使用鎖(做了雙重檢測),具體方法:
來測試一下,測試資料、次數和1.2的GetDescriptionOriginal_Test相同,效率有很大的提升,隻有一次記憶體回收。
還是先看看實作方法吧!
假設我們的使用場景是這樣的:項目定義的枚舉并不多,但是用其描述值很頻繁,比如定義了一個使用者性别枚舉,用的地方很多,使用頻率很高。
上面GetDescriptionByDictionaryWithLocak的方法中,第一句代碼“if (_LockDictionary.ContainsKey(@this)) ”就是驗證是否包含枚舉值。在2.1的測試中執行了8000w次,其中隻有80次(總共隻有80個枚舉值用于測試)需要這句代碼“if (_LockDictionary.ContainsKey(@this)) ”,其餘的直接取值就可了。基于這樣的考慮,就有了上面的方法GetDescriptionByDictionaryWithException。
來測試一下,看看效果吧!
測試結果來看,基本上差不多,在時間上略微快樂一點點,1,208.0000ms:1,860.0000ms,執行8000w次快600毫秒,好像差别也不大啊,這是為什麼呢?
這個其實就是Dictionary的問題了,Dictionary内部使用雜湊演算法計算存儲位址,其查找的時間複雜度為o(1),他的查找效果是非常快的,而本方法中利用了異常處理,異常捕獲本身是有一定性能影響的。
ConcurrentDictionary是一個線程安全的字典類,代碼:
測試代碼及測試結果:
綜上所述,解決了性能問題、位域枚舉問題的正式的代碼:
版權所有,文章來源:http://www.cnblogs.com/anding
個人能力有限,本文内容僅供學習、探讨,歡迎指正、交流。
1.EnumExtension.cs代碼:

View Code
2.測試類EnumTest.cs代碼:

3.輔助測試類TestHelper.cs
