C # obtain and parse descriptionattribute of Enumeration type

Source: Internet
Author: User

The attribute system. componentmodel. descriptionattribute is often used to provide description for attributes or events. This description can be localized. In some user interfaces, you can use this attribute to provide some additional information, as shown in 1 in Visual Studio:

Figure 1 shows the description of autosizemode in the following box.

However, the enumeration items in the interface are not so well treated. The C # class library does not have built-in support for the descriptionattribute of enumeration items, as shown in the figure above, the enumerated items are still in English. To provide the desired description, you must complete it yourself.

1. Simple implementation

This function is actually very easy to implement, that is, to read the value of the description attribute of descriptionattribute through reflection,CodeAs follows:

/// <Summary> /// return the description of the enumerated items. /// </Summary> /// <Param name = "value"> enumeration item for obtaining the description. </Param> /// <returns> specifies the description of the enumeration. </Returns> Public static string getdescription (Enum value) {type enumtype = value. GetType (); // obtain the enumerated constant name. String name = enum. getname (enumtype, value); If (name! = NULL) {// obtain the enumerated field. Fieldinfo = enumtype. getfield (name); If (fieldinfo! = NULL) {// gets the description attributes. Descriptionattribute ATTR = attribute. getcustomattribute (fieldinfo, typeof (descriptionattribute), false) as descriptionattribute; If (ATTR! = NULL) {return ATTR. Description ;}} return NULL ;}

This code is easy to understand. The name used to obtain the enumerated constant here isEnum. getname() Instead of tostring (), because the former is faster, and null is returned for values that are not enumeration constants, no additional reflection is required.

Of course, this code is only a simple example, and will be analyzed in more detail later.

Ii. Complete implementation

Before providing a more complete implementation, let's talk about the descriptionattribute problem.

In my opinion, for enumeration, this description is more like a localized and more friendlyAliasInstead of an explanation or description. Take the autosizemode enumeration in the starting image as an example. What we want to see is "auto expand or contract" and "only expand ", as described in msdn, "The control increases or decreases based on its content. You cannot manually adjust the size of the control ." And "the control can increase at will based on its content, but it will not be smaller than its size attribute value. The form size can be adjusted, but it cannot be reduced to hide any controls it contains ."

Therefore, displaynameattribute is more suitable than descriptionattribute. However, displaynameattribute can only be used for classes, methods, attributes, or events. A field is ruthlessly discarded by it. Therefore, only descriptionattribute, which is not suitable, can be used to join in.

Speak out and start talking about things. First of all, the above function is still very rough and is not considered in many cases. For example, if the given value does not correspond to an enumeration constant, what should we do?

First, refer to how Microsoft works. The following describes how enum. tostring () works:

    • If the flags flag is applied and a combination of one or more named constants equal to the value of the instance exists, a list of constant names separated by delimiters is returned. If
    • If the Instance value cannot be equal to the combination of named constants, the original value is returned.
    • If the flags flag is not applied, the original value is returned.

So I will adopt a similar approach, but the Instance value cannot be equal to the combination of named constants (the second point above ), it will return the constant name that can be matched + the number value that is not matched, not just the number value, which makes it easier for me.

Taking the bindingflags enumeration as an example, for the value 129, if enum. tostring () is used directly, 129 is returned directly, but I think returning ignorecase and 128 is a better choice.

run the following code:

/// <Summary> /// return the description of the specified enumerated value (specified by // <see CREF = "system. componentmodel. descriptionattribute"/> ). /// If no description is specified, the name of the enumerated constant is returned. If no enumerated constant is found, the enumerated value is returned. /// </Summary> /// <Param name = "value"> obtain the enumerated value of the description. </Param> /// <returns> specifies the description of the enumerated value. </Returns> Public static string getdescription (this Enum value) {type enumtype = value. GetType (); // you can specify a combination of enumerated values. Enumcache cache = getenumcache (enumtype. typehandle); ulong valueul = touint64 (value); int idx = array. binarysearch (cache. values, valueul); If (idx> = 0) {// The enumerated value has been defined and the corresponding description is directly returned. Return cache. Descriptions [idx];} // if it is not an enumerative that can be combined, the enumerated string is directly returned. If (! Cache. hasflagsattriul) {return getstringvalue (enumtype, valueul);} List <string> List = new list <string> (); // search for matching binary data from the back and forth. For (INT I = cache. Values. Length-1; I> = 0 & valueul! = 0ul; I --) {ulong enumvalue = cache. values [I]; If (enumvalue = 0ul) {continue;} If (valueul & enumvalue) = enumvalue) {valueul-= enumvalue; List. add (cache. descriptions [I]) ;}} list. reverse (); // Add the last undefined value. If (list. Count = 0 | valueul! = 0ul) {list. Add (getstringvalue (enumtype, valueul);} return string. Join (",", list );}

The getenumcache in the Code returns the values of the specific enumeration type and the corresponding description of the cache, which can avoid reflection every time and significantly improve the performance.

Uint64 is used to compare all enumerated values, which makes it easier to write code (more convenient to write code with an object directly), and the timeliness of binary search is higher.

For the enumeration with the flags flag applied, binary matching is performed from backward to forward (note that values is sorted in ascending order), and then reverse at the end to obtain the enum. the order of tostring () is the same.

The getstringvalue method obtains the number corresponding to the enumerated value. However, you cannot directly use tostring () because the enumerated value can beNegativeTo ensure that the output value is the same as the definition value, you must determine whether to convert the value to int64 and then output the data based on the Basic Enumeration type.

Iii. Enumeration Parsing

Now we can get the corresponding Description Based on enumeration, and then we need to complete its inverse process-resolution. The parsing process is roughly the following four steps:

    1. Try to parse the string as a number. If it is successful, you don't have to perform a more costly string match. Here we need to be able to parse integers with positive and negative numbers, and the maximum value needs to be able to parse integers in the uint64 range, so here, based on whether the first character of the string is "-", to determine whether to use the int64.tryparse method or the uint64.tryparse method.
    2. Separate strings with commas (,) as a string array. Here, we usually use string. split (',') is used to separate strings. However, this is very inefficient, and a trim () operation is required to remove white space. Therefore, additional string copies are generated. Therefore, I directly use indexof () + substring () for implementation, which is more efficient and not complicated.
    3. Parse each string in the array and try to match the enumerated constant or description. Here we will compare the strings obtained in the previous step with the enumerated cache one by one. To support enumeration constants and descriptions, we need to compare strings twice. The first time is compared with enumeration constants, and the second time is compared with the description. The dictionary is not used here, mainly because it requires two (case-insensitive and case-insensitive) dictionaries, and it is not worthwhile. Generally, the enumerated constants are within 10, so sequential search is not slow.
    4. If the match fails, try to recognize each array as a number. This is to ensure that the string obtained by the getdescription method can be correctly parsed.

The parsing method code is as follows:

Public static object parseex (type enumtype, string value, bool ignorecase) {exceptionhelper. checkargumentnull (enumtype, "enumtype"); exceptionhelper. checkargumentnull (value, "value"); If (! Enumtype. isenum) {Throw exceptionhelper. mustbeenum (enumtype);} value = value. trim (); If (value. length = 0) {Throw exceptionhelper. mustcontainenuminfo () ;}// try to parse the number to avoid subsequent string comparison. Char firstchar = value [0]; ulong tmpvalue; If (parsestring (value, out tmpvalue) {return enum. toobject (enumtype, tmpvalue);} // try to parse the description. Enumcache cache = getenumcache (enumtype. typehandle); stringcomparison comparison = ignorecase? Stringcomparison. ordinalignorecase: stringcomparison. ordinal; ulong valueul = 0; int start = 0; do {// remove leading white space. While (char. iswhitespace (value, start) {start ++;} int idx = value. indexof (',', start); If (idx <0) {idx = value. length;} int nidx = idx-1; // remove the white space. While (char. iswhitespace (value, nidx) {nidx --;} If (nidx> = Start) {string STR = value. substring (START, nidx-start + 1); Int J = 0; // compare the names and descriptions of common values. Compare the names and descriptions. For (; j <cache. names. length; j ++) {If (string. equals (STR, cache. names [J], comparison) {// matches with a constant value. Valueul | = cache. Values [J]; break ;}}if (j = cache. Names. Length & cache. hasdescription) {// compare description information. For (j = 0; j <cache. descriptions. length; j ++) {If (string. equals (STR, cache. descriptions [J], comparison) {// match the description information. Valueul | = cache. Values [J]; break ;}}// unrecognized enumerated value. If (j = cache. Descriptions. Length) {// try to recognize it as a number. If (parsestring (STR, out tmpvalue) {valueul | = tmpvalue;} else {// cannot be recognized as a number. Throw predictionhelper. enumvaluenotfound (enumtype, STR) ;}} start = idx + 1 ;}while (start <value. Length); Return enum. toobject (enumtype, valueul );}
4. Display enumeration instructions in propertygrid

To display object properties on the interface, the commonly used control is propertygrid. If you want the description of enumeration to be displayed in propertygrid, you can use typeconverterattribute to do this.

First, you must define an enumdescconverter class that supports reading enumeration instructions. It can be directly inherited from the typeconverter class or enumconverter class. What it needs to do is to use getdescription () instead of tostring () to convert the enumerated value to a string (convertor (). In convertfrom, you must also support enumeration description parsing.

Using system; using system. componentmodel; using system. globalization; namespace cyjb. componentmodel {// <summary> // provide the <see CREF = "system. enum "/> type converter for converting objects and other forms of representation. /// Description of enumerated values is supported. /// </Summary> public class enumdescconverter: enumconverter {// <summary> // use the specified type to initialize a new instance of the <see CREF = "enumdescconverter"/> class. /// </Summary> /// <Param name = "type"> indicates the enumerated type associated with this converter. </Param> Public enumdescconverter (type): Base (type) {}/// <summary> // converts a specified value object to an enumeration object. /// </Summary> /// <Param name = "context"> <see CREF = "system. componentmodel. itypedescriptorcontext"/>, // provides the format context. </Param> /// <Param name = "culture"> an optional <see CREF = "system. Globalization. cultureinfo"/>. /// If no regional settings are provided, the current culture is used. </Param> /// <Param name = "value"> <see CREF = "system. Object"/> to be converted. </Param> // <returns> indicates <see CREF = "system. Object"/> of the converted <paramref name = "value"/>. </Returns> Public override object convertfrom (itypedescriptorcontext context, cultureinfo culture, object Value) {string strvalue = value as string; If (strvalue! = NULL) {try {return enumext. parseex (this. enumtype, strvalue, true);} catch (exception ex) {Throw exceptionhelper. convertinvalidvalue (value, this. enumtype, ex) ;}} return base. convertfrom (context, culture, value) ;}/// <summary> /// convert a given value object to a specified target type. /// </Summary> /// <Param name = "context"> <see CREF = "system. componentmodel. itypedescriptorcontext"/>, // provides the format context. </Param> /// <Param name = "culture"> an optional <see CREF = "system. Globalization. cultureinfo"/>. /// If no regional settings are provided, the current culture is used. </Param> /// <Param name = "value"> <see CREF = "system. Object"/> to be converted. </Param> // <Param name = "destinationtype"> <see CREF = "system. Type"/> to convert the value. </Param> // <returns> indicates <see CREF = "system. Object"/> of the converted <paramref name = "value"/>. </Returns> Public override object convertion (itypedescriptorcontext context, cultureinfo culture, object value, type destinationtype) {predictionhelper. checkargumentnull (destinationtype, "destinationtype"); If (value! = NULL & destinationtype. typehandle. equals (typeof (string ). typehandle) {return enumext. getdescription (Enum) value);} return base. convertize (context, culture, value, destinationtype );}}}

Then, the [typeconverter (enumdescconverter)] is used to identify its own converter class on the required properties, so that what is displayed on the propertygrid is what you want to explain.

 
Public class testclass {[typeconverter (typeof (enumdescconverter)] public tristate value {Get; Set ;}// the tristate here is an enumeration that applies descriptionattribute .}

Figure 2 the enumerated values displayed on the page are correctly displayed in Chinese.

Link to the relevant code:

Complete code for class enumext containing enumeration-related methods visible https://github.com/CYJB/Cyjb/blob/master/Cyjb/EnumExt.cs

Enumdescconverter visible https://github.com/CYJB/Cyjb/blob/master/Cyjb/ComponentModel/EnumDescConverter.cs above

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.