If you do not know much about reflection, read this blog to get a rough idea of reflection. What is reflection?
Attribute)
1. C # built-in features
A feature is an object that can be loadedProgramIn the set and Assembly objects, these objects include the Assembly itself, modules, classes, interfaces, structures, constructors, methods, and method parameters. The objects loaded with features are called feature targets. A feature is a mechanism for adding metadata (data describing data) to a program. It can provide instructions to the compiler or instructions on data.
Note: The English name of a feature is attribute. In some books, it is translated as "attribute". In other books, it is translated as "feature "; because we usually call the class members that contain get and/or set accessors as "properties", I will use the term "feature" in this article, to distinguish "property ).
The above prompt is in Vs, which should have been encountered during programming.
Next we will introduce the first feature
1.1 system. obsoleteattribute
Let's show this example to see how the feature solves the above problem: we can add the obsolete feature to the old sendmsg () method to tell the compiler that this method is outdated, then, when the compiler finds that the obsolete-tagged method is used in the program, a warning is given.
Namespace testobsolete {class program {public class message {} public class testclass {// Add the obsolete feature [obsolete ("Please use the new sendmsg (Message MSG) overload method")] public static void showmsg () {console. writeline ("this is the old sendmsg () method");} public static void showmsg (Message MSG) {console. writeline ("New sendmsg () method") ;}} static void main (string [] ARGs) {testclass. showmsg (); testclass. showmsg (New message ());}}}
SimpleCodeAs shown above. Now, when running this code, we will find that the compiler has given a warning:Warning cs0618: "attribute. testclass. showmsg ()" expired: "Use the new sendmsg (Message MSG) overload method ".By using the features, we can see that the compiler provides a warning to tell the customer that there is a new method available for use, so that after the programmer sees this warning information, the new sendmsg () method is considered.
1.2 usage of features
Through the above example, we have roughly seen how to use the feature: first, there is a square brackets "[]", followed by the feature name after the left square brackets "[", such as obsolete, then there is a parentheses "()". Unlike a common class, this parentheses can not only write constructor parameters, but also assign values to class attributes. In the obsolete example, only the constructor parameters are passed.
Note:
In fact, when you select obsolete with the mouse box and press F12 to define it, you will find that its full name is obsoleteattribute, which inherits from the attribute class. But here we only use obsolete to mark the method. This is. net convention, all features should end with attribute. If the attribute is not added when marking the object, the compiler will automatically find the version with attribute.
When using constructor parameters, the order of parameters must be the same as that of the constructor declaration. All parameters are also called positional parameters, attribute parameters are also called named parameters ). It will be described in detail below.
2. Custom features (custom attributes)
2.1 example
If you cannot define a feature and use it by yourself, I don't think you can understand it very well. Now let's build a feature by ourselves. Suppose we have a very common requirement: When we create or update a class file, we need to explain when and who created the class, in future updates, we also need to explain when the updated content will be updated. You can record or not record the updated content. What would you do in the past? Is it like this to add a comment to the class above the class:
[Record ("Update", "Leo", "2013-3-20", memo = "Modify ...... ")] [Record (" CREATE "," Amy "," 2013-3-10 ")] [record (" Update "," aehyok "," 2013-3-18 ")] public class democlass {public override string tostring () {return "this is a demo class ";}}
This can indeed be recorded, but what if one day we want to save these records to the database for backup? Do you want to view the source files one by one, find out the comments, and insert them into the database one by one?
Through the definition of the above feature, we know that the feature can be used to add metadata to the type, which can be used to describe the type. In this case, features should be used. In this example, the metadata should be: annotation type ("Update" or "CREATE"), modifier, date, and remarks (optional ). The target type of the feature is the democlass class.
Based on our understanding of the metadata appended to the democlass class, we first create a class recordattribute that encapsulates the metadata:
Public class recordattribute {private string recordtype; // record type: update/create private string author; // author private datetime date; // update/create date // constructor, the parameters of the constructor are also called "location parameters" in the feature ". Public recordattribute (string recordtype, string author, string date) {This. recordtype = recordtype; this. author = author; this. date = convert. todatetime (date) ;}// for location parameters, only the Public String recordtype {get {return recordtype ;}} Public String author {get {return author ;}} is provided ;}} public datetime Date {get {return date ;}// Public String memo {Get; Set ;}}
This class not only looks like it, but is actually no different from a common class. Obviously, it cannot be changed to a feature because it is followed by an attribute. So how can we call it a feature and apply it to a class? Before proceeding to the next step, let's take a look at how the built-in features of. NET are defined:
// Abstract: // mark the program elements that are no longer in use. This class cannot be inherited. [Serializable] [comvisible (true)] [attributeusage (attributetargets. class | attributetargets. struct | attributetargets. enum | attributetargets. constructor | attributetargets. method | attributetargets. property | attributetargets. FIELD | attributetargets. event | attributetargets. interface | attributetargets. delegate, inherited = false)] public sealed class obsoleteattribute: attribute {// Abstract: // use The default attribute initializes a new instance of the system. obsoleteattribute class. Public obsoleteattribute (); // Abstract: // use the specified work und to initialize a new instance of the system. obsoleteattribute class. //// Parameter: // message: // text string that describes the optional work und. Public obsoleteattribute (string message); // Abstract: // use the work und message and Boolean value to initialize the system. the new instance of the obsoleteattribute class. This Boolean value indicates whether to treat obsolete elements as errors. //// Parameter: // message: // text string that describes the optional work und. //// Error: // indicates whether to treat obsolete elements as incorrect boolean values. Public obsoleteattribute (string message, bool error); // Abstract: // obtain a Boolean value indicating whether the compiler treats obsolete program elements as incorrect. //// Return result: // If the obsolete element is regarded as an error, true is returned; otherwise, false is returned. The default value is false. Public bool iserror {Get;} /// Abstract: // obtain the work und message, including description of the optional program elements. //// Return result: // The variable Text string. Public String message {Get ;}}}
2.2 format of adding features (location parameters and naming parameters)
First, we should find that it inherits from the attribute class, which means that our recordattribute should also inherit from the attribute class.
Secondly, we found that three features are used to describe this feature. These three features are: serializable, attributeusage, and comvisible. The serializable feature should be mainly used for serialization. comvisible is simply "control the accessibility of individual hosted types, members, or all types of COM in a program set" (defined by Microsoft ). We should note that:Features are used to describe the metadata of data, and these three features are used to describe features, so they can be considered as "metadata" (meta-metadata ).
Because we need to use "meta data" to describe our defined feature recordattribute, so now we need to first understand "meta data ". Here we should remember that "meta-data" is also a feature. In most cases, we only need to master attributeusage, so now let's take a look at it. First, let's take a look at how attributeusage is loaded to the obsoleteattribute feature.
[Attributeusage (8192, inherited = false)]
Then let's take a look at the definition of attributeusage:
// Abstract: // specify the usage of another special class. This class cannot be inherited. [Serializable] [comvisible (true)] [attributeusage (attributetargets. class, inherited = true)] public sealed class attributeusageattribute: attribute {// Abstract: // use the specified system. attributetargets, system. attributeusageattribute. allowmultiple // value and system. attributeusageattribute. the inherited Value List initializes system. attributeusageattribute // a new instance of the class. //// Parameter: // validon: // a set of values combined with the bitwise "or" operator to indicate which program elements are valid. Public attributeusageattribute (attributetargets validon); // Summary: // gets or sets a Boolean value indicating whether multiple indicator feature instances can be specified for a program element. //// Return result: // true if multiple instances are allowed; otherwise, false. The default value is false. Public bool allowmultiple {Get; set;} // Summary: // gets or sets a Boolean value, which indicates whether the property can be inherited by a derived class or override member. //// Return result: // if this feature can be inherited by a derived class or override Member, true is returned; otherwise, false is returned. The default value is true. Public bool inherited {Get; set;} // Summary: // gets a set of values, which identify the program elements that can be applied to the characteristics indicated by this set of values. //// Return result: // one or more system. attributetargets values. The default value is all. Public attributetargets validon {Get ;}}
As you can see, it has a constructor that contains a location parameter of the attributetargets type (positional parameter ). Note that the validon attribute is not a named parameter because it does not contain the set accessors.
Here, you may wonder why parameters are divided in this way, which is related to the use of features. If attributeusageattribute is a common class, we must use it like this:
// Instantiate an attributeusageattribute usage = new attributeusageattribute (metadata. Class); usage. allowmultiple = true; // set the allowmutiple attribute usage. inherited = false; // set the inherited attribute.
However, the feature is written as only one line of code and then closely related to the type (target type) applied by the feature. What should I do? Microsoft's software engineers came up with the following method: no matter the parameters or attributes of the constructor, they are all written into the parentheses of the constructor. For the constructor parameters, the parameters must follow the order and type of the constructor parameters. For properties, they are separated by commas (,) in the format of "property = value. The code above is reduced to the following:
[Attributeusage (attributetargets. Class, allowmutiple = true, inherited = false)]
It can be seen that attributetargets. Class is a constructor parameter (location parameter), while allowmutiple and inherited are actually attributes (named parameters ). The name parameter is optional. In the future, our recordattribute will be used in the same way. (Why do they call parameters? I guess they look more like method parameters .)
Assume that our recordattribute has been OK, and its usage should be as follows:
[Record ("CREATE", "Amy", "2013-3-10", "create test")] public class democlass
Recordtype, author, and date are the location parameters, while memo is the naming parameter.
2.3 attributetargets bit mark
We can see from the attributeusage attribute name that it is used to describe the usage of the feature. Specifically, it should first be the types or objects that the marked features can apply. From the code above, we can see that the attributeusage feature constructor accepts a attributetargets type parameter. Now let's take a look at attributetargets.
Attributetargets is a single-digit tag that defines the types and objects that a feature can apply.
// Abstract: // specifies the application elements that can apply features to them. [Serializable] [flags] [comvisible (true)] public Enum attributetargets {// Abstract: // you can apply features to an assembly. Assembly = 1, /// Abstract: // you can apply features to a module. Module = 2, // Summary: // you can apply the class features. Class = 4, // Summary: // you can apply the feature to the structure, that is, the value type. Struct = 8, // Summary: // You can enumerate the application features. Enum = 16, /// Abstract: // you can apply features to constructors. Constructor = 32, /// Abstract: // you can apply features to methods. Method = 64, /// Summary: // you can apply properties. Property = 128, // Summary: // you can apply the features of a field. Field = 256, // Summary: // you can apply the event features. Event = 512, // Summary: // you can apply features to the interface. Interface = 1024, /// Abstract: // you can apply features to parameters. Parameter = 2048, // Abstract: // you can delegate application features. Delegate = 4096, /// Abstract: // you can apply features to the returned values. Returnvalue = 8192, /// Summary: // you can apply features to generic parameters. Genericparameter = 16384, /// Abstract: // you can apply features to any application element. All = 32767 ,}
It is not difficult to understand why the above examples use:
[Attributeusage (attributetargets. Class, allowmutiple = true, inherited = false)]
The attributeusage loaded on the obsoleteattribute feature is as follows:
[Attributeusage (8192, inherited = false)]
Because attributeusage is a single-digit tag, it can be combined by bit or "|. So when we write this:
[Attributeusage (attributetargets. Class | attributetargets. Interface)
This means that features can be applied to both classes and interfaces.
Note:: There are two special cases: observing the attributeusage definition above, it means that the feature can also be loaded to Assembly and module, and these two are our compilation results, this type does not exist in the program. How should we load it? You can use the syntax [Assembly: someattribute (parameter list)]. In addition, this statement must be prior to the start of a Program Statement.
2.4 inherited and allowmutiple attributes
The allowmutiple attribute is used to set whether the feature can be added to a type repeatedly (the default value is false), as shown in the following figure:
[Record ("Update", "Leo", "2013-3-20", memo = "Modify ...... ")] [Record (" Update "," aehyok "," 2013-3-18 ")] [record (" CREATE "," Amy "," 2013-3-10 ")] public class democlass {
Therefore, we must set allowmutiple to true.
Inherited is more complicated. If a class inherits from our democlass, when we add recordattriclass to democlass, The democlass subclass also obtains this feature. When a feature is applied to a method, if the method is overwritten by a subclass inherited from this class, then inherited is used to show whether the subclass method inherits this feature.
In our example, set inherited to false.
2.5 implement recordattribute
Now it is very easy to implement recordattribute. You do not need to make any changes to the class subject. We only need to make it inherit from the attribute base class, you can also mark it with the attributeusage feature (assuming we want to apply this feature to classes and methods ):
[Attributeusage (attributetargets. class | attributetargets. method, allowmultiple = true, inherited = false)] public class recordattriple: attribute {private string recordtype; // record type: update/create private string author; // author private datetime date; // update/create date // constructor. The constructor parameters are also called "location parameters" in features ". Public recordattribute (string recordtype, string author, string date) {This. recordtype = recordtype; this. author = author; this. date = convert. todatetime (date) ;}// for location parameters, only the Public String recordtype {get {return recordtype ;}} Public String author {get {return author ;}} is provided ;}} public datetime Date {get {return date ;}// construct an attribute, which is also called the "name parameter" Public String memo {Get; Set ;}} in the feature ;}}
2.6 Use recordattribute
[Record ("Update", "Leo", "2013-3-20", memo = "Modify ...... ")] [Record (" Update "," aehyok "," 2013-3-18 ")] [record (" CREATE "," Amy "," 2013-3-10 ")] public class democlass {public override string tostring () {return "this is a demo class" ;}} class program {static void main (string [] ARGs) {democlass demo = new democlass (); console. writeline (demo. tostring ());
This program simply outputs a "This is a demo class" on the screen ". Our properties also seem to have no impact on the program as if we used "//" for annotation. In fact, the data we added has been added to the Assembly as metadata.
3. Use reflection to view custom features
Using Reflection to view custom feature information is similar to viewing other information. First, obtain a type object based on the Type (democlass in this example), and then call the getcustomattributes () method of the type object, obtain the features applied to this type. When the first attributetype parameter in getcustomattributes (type attributetype, bool inherit) is specified, only the features of the specified type are returned. Otherwise, all features are returned; the second parameter specifies whether to search for the member's inheritance chain to find these attributes.
Type T = typeof (democlass); console. writeline ("the following lists the recordattrieline attributes applied to {0}:", T); // obtain all recordattributes feature object [] records = T. getcustomattributes (typeof (recordattrites), false); foreach (recordattribute record in Records) {console. writeline ("{0}", record); console. writeline ("type: {0}", record. recordtype); console. writeline ("Author: {0}", record. author); console. writeline ("Date: {0}", record. date . Toshortdatestring (); If (! String. isnullorempty (record. Memo) {console. writeline ("Remarks: {0}", record. Memo );}}
Next let's take a look at the final execution result.
This is the sample code for downloading the connection.