Attribute is a new type of declarative information. you can use attributes to define design-level information (such as a help file or a document link) and runtime information (such as associating an XML file with a class ). you can also use properties to create a "self-description" component. through this tutorial, we will learn how to create and attach attributes to differentProgramObject, and how to find the attribute information during runtime.
Definition
The description of msdn is the additional description of the attribute, which is a detailed description of the Declaration.
Use predefined attributes
In C #, there are a few predefined attributes. Before learning how to create custom attributes, Let's first look at howCodeUse those attributes.
Using system;
Public class anyclass
{
[Obsolete ("Don't use old method, use new method", true)]
Static void old (){}
Static void new (){}
Public static void main ()
{
Old ();
}
}
In this example. we use the obsolete attribute to mark a program entity that should no longer be used. the first parameter is a string used to explain why the item is voided and used instead. in fact, you can write anything here. the second parameter tells the compiler that the place where this item is used should be treated as an error. the default value of this parameter is false, which means that the compiler generates a warning.
When we try to compile the above program, we will get an error:
Anyclass. Old () 'is obsolete: 'don' t use old method, use new Method'
Develop Custom Attributes
Now, we will learn how to develop your own attributes. Here is a tip for creating custom attributes.
According to the C # language specification, our attribute classes are inherited from system. attribute completes our custom attributes (inherited from the abstract class system. attribute to directly or indirectly become an attribute class. the declaration of attribute classes theoretically defines a new attribute that can be placed on the Declaration ).
Using system;
Public class helpattribute: attribute
{
}
Whether you believe it or not, we have correctly created a custom property. We can use the above property to decorate our class just like the obsolete property.
[Help ()]
Public class anyclass
{
}
Note: using the keyword attribute as the suffix of the attribute class name is a convention. however, when we attach an attribute to a program entity, we do not need to include the attribute suffix. the compiler first. attribute. if not found, the compiler adds the keyword attribute to the specified attribute name before searching.
Currently, this attribute is useless. Let's add something to make it useful.
using system;
public class helpattribute: attribute
{< br> Public helpattribute (string description_in)
{< br> This. description = description_in;
}< br> protected string description;
Public String description
{< br> Get
{< br> return this. description;
}
}
}
[Help ("This is a do-nothing class")]
Public class anyclass
{
}
In the above example, we add a property in the property class. In the next section, we will find this property (description) at runtime ).
Define or control the usage of Custom Attributes
Attributeusage is another predefined class, which can help us control the use of custom attribute classes. that is to say, we can define attributes for the custom property class. it explains how to use a custom property class.
When attributeusage is used for custom attributes, you can assign values to the three properties of attributeusage. Next we will discuss these three properties ).
Validon
Through this attribute, we can define custom attributes that can be used on those program bodies. attributetargets enumeration lists all program bodies that can be used by attributes. multiple attributetargets values can be used together using the OR operator.
Allowmultiple
This property indicates whether multiple custom properties can be used on the same program body.
Inherited
This feature indicates whether an attribute can be inherited from a category of a custom attribute.
Let's use the attrubuteusage attribute in the Help attribute class to control the usage of the help attribute class.
using system;
[attributeusage (attributetargets. class),
allowmultiple = false, inherited = false]
public class helpattribute: attribute
{< br> Public helpattriple (string description_in)
{< br> This. description = description_in;
}< br> protected string description;
Public String description
{< br> Get
{< br> return this. description;
}< BR >}< br> first, check attributetargets. class. it specifies that the help attribute can only be used on the class. this means that the following code will generate an error:
anyclass. CS: attribute 'help' is not valid on this declaration type. it is valid on 'class' declarations only.
use this method now.
help ("This is a do-nothing class")]
public class anyclass
{< br> [help ("This is a do-nothing method")] // error
Public void anymethod ()
{< BR >}< br> attributetargets. all allows the help attribute to be applied to any program body (possible values: Assembly, module, class, struct, Enum, constructor, method, property, field, event, interface, parameter, delegate, all = Assembly | module | class | struct | Enum | constructor | method | property | FIELD | event | interface | parameter | delegate, classmembers = Class | struct | Enum | constructor | method | property | FIELD | event | delegate | Interface)
check allowmultiple = false. it specifies that attributes cannot be used on the same program body multiple times.
[help ("This is a do-nothing class")]
[help ("It contains a do-nothing method")]
public class anyclass
{< br> [help ("This is a do-nothing method")] // error
Public void anymethod ()
{< BR >}< br> the code above produces a compile-time error: anyclass. CS: duplicate 'help' attribute
now let's discuss the last property ). inheritance means that when an attribute is placed on the base class, it is also inherited by the class derived from the base class. If the inherited of the attribute class is true, the property can be inherited. however, if the inherited of the attribute class is false or not specified, the property cannot be inherited.
let's look at the result of the following class relationship:
[attributeusage (attributetargets. class, allowmultiple = false, inherited = false]
[attributeusage (attributetargets. class, allowmultiple = true, inherited = false]
[attributeusage (attributetargets. class, allowmultiple = false, inherited = true]
[attributeusage (attributetargets. class, allowmultiple = true, inherited = true]
first case
if we look for the help attribute of the derive class, we cannot find its attribute ), because the value of inherited is false.
second case
the value of inherited is also false.
third case
to explain the 3rd and 4th examples, let's add the same attributes as the base class to the derive class.
[Help ("baseclass")]
Public class base
{
}
[Help ("deriveclass")]
Public class derive: Base
{
}
If we look for the help attribute, we will only get the attribute of the drive class, because the inherited attribute value is true, but multiple attributes are not allowed, therefore, the help attribute of the base class is overwritten by the help attribute of the derive class.
Fourth case
In the 4th example, when we look for the help attribute of the derive class, all attributes will be obtained, because inheritance and multiple attributes are allowed.
Note: The attributeusage attribute is valid only when it is inherited from the system. Attribute Class. Both allowmultiple and inherited are false by default.
Locate parameter pair named Parameters
The positioning parameter is the parameter of the property constructor. these are mandatory values. This parameter must be passed to the constructor no matter what program entity the attribute is placed in. in addition, the named parameter actually uses an optional no-argument constructor.
For more detailed explanation, let's add some other properties to the help property class.
[Attributeusage (attributetargets. Class, allowmultiple = false, inherited = false)]
Public class helpattribute: attribute
{
Public helpattribute (string description_in)
{
This. Description = description_in;
This. Version = "No version is defined for this class ";
}
Protected string description;
Public String description
{
Get
{
Return this. description;
}
}
Protected string version;
Public String version
{
Get
{
Return this. version;
}
// If we ever want our attribute user to set this property,
// We must specify Set Method for it
Set
{
This. Version = value;
}
}
}
[Help ("this is class1")]
Public class class1
{
}
[Help ("this is class2", version = "1.0")]
Public class class2
{
}
[help ("this is class3", version = "2.0", description = "this is do-nothing class")]
public class class3
{< BR >}< br> when we look for the help attribute of class1 and its properties, we will get:
help. description: This is class1
help. version: No version is defined for this class
because we have not specified any value for the version attribute (property), we use the value given in the constructor. if no value is specified, the default value of the type is used (for example, in this example, the default value is 0 ).
now, you can find the Custom Attributes and their properties of class2. the following result is displayed:
help. description: This is class2
help. version: 1.0
do not use multiple constructors for optional parameters. name parameters should be used instead. when we assign values to constructors, we need their names, which is why we call them names. for example, in the 2nd example, we define the help attribute class.
[help ("this is class2", version = "1.0")]
In the attributeusage example, validon is the positioning parameter, and inherited and allowmultiple are the naming parameters.
Note: assign values to the named parameters in the property constructor. we need to provide the set accessors of the property.
'version': named attribute argument can't be a read-only property
In class3, what are the results of searching for the help attribute and its properties? The result is also a compilation error.
'description': named attribute argument can't be a read-only property
modify the Hele attribute class to add a set accesser for description. the output is:
help. description: This is do-nothing class help. version: 2.0
in the background, the first constructor is called using the positioning parameter, and the Set accessors named parameters are called in sequence. the value assigned in the constructor is overwritten by the value assigned in the Set accessors.
parameter type
the parameter type of the attribute class is limited to the following types:
Bool, byte, Char, double, float, Int, long, short, string
System. Type
Object
An Enum type, provided that it and any types in which it is nested are publicly accessible
A one-dimen1_array involving any of the types listed above
Attribute identifier
If we want to apply the help attribute to the entire assembly, the first problem that arises is: we need to determine where the help attribute is located before the compiler can identify that this is applied to the entire assembly? Another case: How does the compiler identify that an attribute is applied to a method with a return type, not to all methods?
We use Attribute identifiers to solve this ambiguity. Through attribute identifiers, we can clearly state the entity to which the attribute is applied.
For example:
[Assembly: Help ("this a do-nothing assembly")]
The Assembly identifier before the help attribute explicitly tells the compiler that the current attribute is applied to the entire assembly. The possible identifier is:
Assembly
Module
Type
Method
Property
Event
Field
Param
Return
Search for properties at runtime
We have learned how to create attributes and apply them to program units. Now we will learn how to find these information at runtime.
We can use the reflection APIs of the. NET Framework to iterate through the Assembly metadata and generate a list of classes, types, and methods defined by the Assembly.
Do you still remember the help attribute and anyclass class?
Using system;
Using system. reflection;
Using system. diagnostics;
// Attaching help attribute to entire assembly
[Assembly: Help ("this Assembly demonstrates Custom Attributes creation and their Run-Time query.")]
// Our custom attribute class
Public class helpattribute: attribute
{
Public helpattribute (string description_in)
{
//
// Todo: Add constructor logic here
This. Description = description_in;
//
}
Protected string description;
Public String description
{
Get
{
Return this. description;
}
}
}
// Attaching the help attribute to our anyclass
[Helpstring ("This is a do-nothing class.")]
Public class anyclass
{
// Attaching the help attribute to our anymethod
[Help ("This is a do-nothing method.")]
Public void anymethod ()
{
}
// Attaching help attribute to our anyint Field
[Help ("this is any integer.")]
Public int anyint;
}
Class queryapp
{
Public static void main ()
{
}
}
In the next 2 sections, we will add the property search code in the main method.
Query Assembly attributes
In the following code, we get the current process name and use the loadfrom method of the Assembly class to load the assembly. then, use the getcustomattributes method to obtain all the Custom Attributes attached to the current set. next, traverse all the attributes and try to convert the attributes to the help attribute (if the conversion is illegal, it is advantageous to use the as keyword to convert the object, so we don't have to worry about throwing an exception, because the converted result value is null ). the following row checks whether the conversion is legal and whether the conversion result is null, and then displays the help attribute.
Class queryapp
{
Public static void main ()
{
Helpattribute helpattr;
// Querying Assembly attributes
String assemblyname;
PROCESS p = process. getcurrentprocess ();
Assemblyname = P. processname + ". EXE ";
Assembly A = assembly. loadfrom (assemblyname );
foreach (attribute ATTR in A. getcustomattributes (true)
{< br> helpattr = ATTR as helpattribute;
If (null! = Helpattr)
{< br> console. writeline ("Description of {0 }:\ n {1}",
assemblyname, helpattr. description);
}< BR >}< br> the output result of the above program is:
description of queryattribute.exe: this Assembly demonstrates Custom Attributes creation and their Run-Time query.
press any key to continue.
Search Class, method, and field attribute
In the following code, the only strange thing is the first line of the main method.
Type type = typeof (anyclass );
The typeof operator is used to obtain the type of the anyclass class. The rest of the code is the same as the previous code to find the attributes of the class, so there is no need to explain it.
To search for the attributes of methods and fields, you must first obtain all the methods and fields defined in the class, and then search for the associated attributes like the class attributes.
Class queryapp
{
Public static void main ()
{
Type type = typeof (anyclass );
Helpattribute helpattr;
// Querying class attributes
Foreach (attribute ATTR in type. getcustomattributes (true ))
{
Helpattr = ATTR as helpattribute;
If (null! = Helpattr)
{
Console. writeline ("Description of anyclass: \ n {0}", helpattr. Description );
}
}
// Querying class-method attributes
Foreach (methodinfo method in type. getmethods ())
{
Foreach (attribute ATTR in method. getcustomattributes (true ))
{
Helpattr = ATTR as helpattribute;
If (null! = Helpattr)
{
Console. writeline ("Description of {0 }:\ n {1}", method. Name, helpattr. Description );
}
}
}
// Querying class-field (only public) attributes
Foreach (fieldinfo field in type. getfields ())
{
Foreach (attribute ATTR in field. getcustomattributes (true ))
{
Helpattr = ATTR as helpattribute;
If (null! = Helpattr)
{
Console. writeline ("Description of {0 }:\ n {1}", field. Name, helpattr. Description );
}
}
}
}
}
The output result of the above program is:
Description of anyclass: This is a do-nothing class.
Description of anymethod: This is a do-nothing method.
Description of anyint: This is any integer.
Press any key to continue