. NET multiple methods for obtaining enumeration DescriptionAttribute description information performance improvement, descriptionattribute

Source: Internet
Author: User

. NET multiple methods for obtaining enumeration DescriptionAttribute description information performance improvement, descriptionattribute

I. general use of DescriptionAttribute

1.1 example

The DescriptionAttribute feature can be used in many places. The more common is enumeration. by obtaining the description defined on enumeration, it is displayed on the UI. A simple enumeration definition:

Public enum EnumGender {None, [System. ComponentModel. Description ("Male")] Male, [System. ComponentModel. Description ("Female")] Female, Other ,}

This article does not discuss other DescriptionAttribute application scenarios, nor focuses on the implementation of multiple languages. It only studies how to obtain enumeration description information.

Generally, the following methods are commonly used to obtain enumeration descriptions. You can search for similar code in the garden.

public static string GetDescriptionOriginal(this Enum @this){var name = @this.ToString();var field = @this.GetType().GetField(name);if (field == null) return name;var att = System.Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute), false);return att == null ? field.Name : ((DescriptionAttribute)att).Description;}

In a simple test:

Console. writeLine (EnumGender. female. getDescriptionOriginal (); Console. writeLine (EnumGender. male. getDescriptionOriginal (); Console. writeLine (EnumGender. other. getDescriptionOriginal (); // output result: Female Other

1.2 Implementation Code problems above

What are the features first?

Features:

Attribute is a piece of configuration information associated with a target object, stored in the dll metadata. It is meaningless. You can obtain the configuration feature information through reflection.

Therefore, the main problem is the serious performance problems caused by reflection:

• 1. Reflection is used for each call, which is slow!
• 2. Each reflection call generates a new DescriptionAttribute object, even the same enumerated value. Memory and GC are greatly wasted!
• 3. It seems that bitfield combination objects are not supported!
• 4. the method parameter here is Enum, and Enum is the base class of enumeration. It is a reference type and enumeration is a value type. This method will cause packing, however, this problem seems inevitable.

How poor is the performance? Code to measure the test:

[Test] public void GetDescriptionOriginal_Test () {var enums = this. getTestEnums (); Console. writeLine (enums. count); TestHelper. invokeAndWriteAll () => {System. threading. tasks. parallel. for (0, 1000000, (I, obj) => {foreach (var item in enums) {var a = item. getDescriptionOriginal () ;}}) ;}// output result: 80 TimeSpan: 79,881.0000 ms // a total of nearly 80 seconds of MemoryUsed:-1,652.7970 KBCollectionCount (0 ): 7,990.00 // generation 0 GC recycled more than times, because a large number of DescriptionAttribute objects were created

Here, this. GetTestEnums (); method obtains an enumeration value set for testing. The set size is 80, and times are executed, which is equivalent to times of GetDescriptionOriginal.

TestHelper. the InvokeAndWriteAll method is used to calculate the time before and after execution, memory consumption, and the number of times the 0-generation GC is recycled. The code is provided in the appendix at the end of this article, the memory consumption calculation is not accurate, but you can refer to the number of GC collections in the third indicator.

Ii. Improved DescriptionAttribute Method

Once you understand the cause of the problem, you can solve the problem. The basic idea is to cache the obtained text values. One enumerated value is only reflected once, so that the performance problem is solved.

2.1 use dictionary cache + lock

Because the static variable dictionary is used to cache values, thread security is involved, and a lock (dual detection) is required. The specific method is as follows:

private static Dictionary<Enum, string> _LockDictionary = new Dictionary<Enum, string>();public static string GetDescriptionByDictionaryWithLocak(this Enum @this){if (_LockDictionary.ContainsKey(@this)) return _LockDictionary[@this];Monitor.Enter(_obj);if (!_LockDictionary.ContainsKey(@this)){var value = @this.GetDescriptionOriginal();_LockDictionary.Add(@this, value);}Monitor.Exit(_obj);return _LockDictionary[@this];} 

To test it, the test data and times are the same as the GetDescriptionOriginal_Test of 1.2, and the efficiency is greatly improved, with only one memory recycle.

[Test] public void GetDescriptionByDictionaryWithLocak_Test () {var enums = this. getTestEnums (); Console. writeLine (enums. count) TestHelper. invokeAndWriteAll () => {System. threading. tasks. parallel. for (0, 1000000, (I, obj) => {foreach (var item in enums) {var a = item. getDescriptionByDictionaryWithLocak () ;}});} // Test Result: 80 TimeSpan: 1,860.0000 msMemoryUsed: 159.2422 KBCollectionCount (0): 1.00

2.2 dictionary cache + exception (unusual)

Let's take a look at the implementation method first!

private static Dictionary<Enum, string> _ExceptionDictionary = new Dictionary<Enum, string>();public static string GetDescriptionByDictionaryWithException(this Enum @this){try{return _ExceptionDictionary[@this];}catch (KeyNotFoundException){Monitor.Enter(_obj);if (!_ExceptionDictionary.ContainsKey(@this)){var value = @this.GetDescriptionOriginal();_ExceptionDictionary.Add(@this, value);}Monitor.Exit(_obj);return _ExceptionDictionary[@this];}}

Suppose our application scenario is as follows: there are not many project-defined enumerations, but their description values are frequent. For example, if a user gender enumeration is defined, there are many scenarios to use, frequently used.

In the above GetDescriptionByDictionaryWithLocak method, the first line of code "if (_ LockDictionary. ContainsKey (@ this)" is to verify whether it contains enumeration values. 2.1 tests were performed times, of which only 80 (a total of 80 enumerated values are used for testing) require the code "if (_ LockDictionary. containsKey (@ this) ", and the other values can be directly set. Based on this consideration, the above method GetDescriptionByDictionaryWithException is available.

Let's test the effect!

[Test] public void GetDescriptionByDictionaryWithException_Test () {var enums = this. getTestEnums (); Console. writeLine (enums. count); TestHelper. invokeAndWriteAll () => {System. threading. tasks. parallel. for (0, 1000000, (I, obj) => {foreach (var item in enums) {var a = item. getDescriptionByDictionaryWithException () ;}) ;}// Test Result: 80 TimeSpan: 1,208.0000 msMemoryUsed: 230.9453 KBCollectionCount (0): 1.00

According to the test results, the time is almost the same. It takes a little bit of time, 1,208.0000 ms: 1,860.0000 ms, and the execution speed is 600 ms. It seems that the difference is not big. Why?

This is actually a problem with Dictionary. Dictionary uses the hash algorithm to calculate the storage address, and its search time complexity is o (1). the query result is very fast, exception Handling is used in this method, and exception capture has a certain performance impact.

2.3 Recommended simple solution: ConcurrentDictionary

ConcurrentDictionary is a thread-safe dictionary class with the code:

private static ConcurrentDictionary<Enum, string> _ConcurrentDictionary = new ConcurrentDictionary<Enum, string>();public static string GetDescriptionByConcurrentDictionary(this Enum @this){return _ConcurrentDictionary.GetOrAdd(@this, (key) =>{var type = key.GetType();var field = type.GetField(key.ToString());return field == null ? key.ToString() : GetDescription(field);});}

Test code and test result:

[Test] public void GetDescriptionByConcurrentDictionary_Test () {var enums = this. getTestEnums (); Console. writeLine (enums. count); TestHelper. invokeAndWriteAll () => {System. threading. tasks. parallel. for (0, 1000000, (I, obj) => {foreach (var item in enums) {var a = item. getDescriptionByConcurrentDictionary () ;}}) ;}// Test Result: 80 TimeSpan: 1,303.0000 msMemoryUsed: 198.0859 KBCollectionCount (0): 1.00

2.4 official code

In conclusion, the formal code solves the performance and bit domain enumeration problems:

/// <Summary> /// obtain the description of the enumeration (Descripion ). /// Supports bit fields. If it is a bit field combination value, multiple fields are combined by separators. /// </Summary> public static string GetDescription (this Enum @ this) {return _ ConcurrentDictionary. getOrAdd (@ this, (key) => {var type = key. getType (); var field = type. getField (key. toString (); // If field is null, it should be a combined bit field value, return field = null? Key. getDescriptions (): GetDescription (field) ;}/// <summary> // gets the description of the bit field enumeration, multiple combinations by separators // </summary> public static string GetDescriptions (this Enum @ this, string separator = ",") {var names = @ this. toString (). split (','); string [] res = new string [names. length]; var type = @ this. getType (); for (int I = 0; I <names. length; I ++) {var field = type. getField (names [I]. trim (); if (field = null) continue; res [I] = GetDescription (field);} return string. join (separator, res);} private static string GetDescription (FieldInfo field) {var att = System. attribute. getCustomAttribute (field, typeof (DescriptionAttribute), false); return att = null? Field. Name: (DescriptionAttribute) att). Description ;}

Ps:. NET gets the description of the enumerated Value

1. Define description for enumeration values

Public enum TimeOfDay {[Description ("Morning")] Moning = 1, [Description ("Afternoon")] Afternoon = 2, [Description ("Evening")] evening = 3 ,}

Ii. Methods for obtaining the description of enumeration values

Public static string GetDescriptionFromEnumValue (Type enumType, object enumValue) {try {object o = Enum. parse (enumType, enumValue. toString (); string name = o. toString (); DescriptionAttribute [] customAttributes = (DescriptionAttribute []) enumType. getField (name ). getCustomAttributes (typeof (DescriptionAttribute), false); if (customAttributes! = Null) & (customAttributes. Length = 1) {return customAttributes [0]. Description;} return name ;}catch {return "unknown ";}}

3. Use of methods for obtaining the description of enumeration values

string strMoning = GetDescriptionFromEnumValue( typeof (TimeOfDay) , 2 );

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.