Abstract
When designing a type, you can use various Members to describe the type of information, but sometimes we may be reluctant to put some additional information into the class, because, it may cause trouble or misunderstanding to the Information Description of the type. We want to add additional information for the type, attribute, method, and return value. These additional information can more clearly express the status of the class and its object members. What should we do? Custom attribute can be used.
To avoid attribute and property translation misunderstanding, attribute is represented in the following discussion.
Careful readers may find similar definitions as follows:
// Assemblyinfo of the project. the CS file contains: [Assembly: GUID ("df510f85-e549-4999-864d-bb892545690b")] [Assembly: assemblyversion ("1.0.0.0")] // You may also find that some classes have similar statements before: [serializable, comvisible (true)] public sealed class string {}// when we develop a WCF project and define an interface contract, a similar statement exists before the interface: [servicecontract] public interface iservice {}
Serializable and servicecontract in square brackets [] are the feature attribute provided by. NET Framework. Some of them apply to the Assembly, some to the class and interface, and some to other Members such as attributes and methods.
Section 1 custom feature attribute
Attribute refers to attaching some declarative description information to a declarative object. After compilation, the self-description information equivalent to the target object is compiled into the metadata of the managed module, obviously, if there are too many descriptions, the volume of metadata will be greatly increased. The compiler only compiles the description information into the metadata, but does not pay attention to the "logic" of attribute.
The assemblyversion, serializable, and servicecontrac mentioned above all inherit from system. attribute Class, CLS requires that custom attribute must inherit from system. attribute Class. In order to comply with the specifications, all custom features must be suffixed with attribute. This is only a specification and can be left empty without being forced. Even if a suffix is used, the C # compiler allows the suffix to be omitted during encoding for convenience. vs smart prompts also provide great support for this.
As follows, we have defined a custom feature of the relevant countries:
public class CountryAttribute : Attribute { public CountryAttribute(string name) { this.Name = name; } public int PlayerCount { get; set; } public string Name { get; set; } }
Let's take a look at what compilation has done:
As you can see, the custom feature is a common class that inherits the system. Attribute Class. There is nothing special about it. A non-Abstract custom feature class must have at least one common constructor, because it takes effect when applying a custom feature to other target elements. Pay attention to the following points for custom features:
(1) Public fields and attributes can be provided within the custom features, but no public methods, events, and other Members should be provided, such as the attribute name and playercount in the code above are allowed.
(2) The common instance constructor can have parameters or no parameters, or provide multiple constructor functions at the same time. The countryattribute class above can add a non-argument constructor:
public CountryAttribute() { }
(3) when defining the parameters, fields, and attributes of the attribute class instance constructor, only the following data types can be used: object, type, String, Boolean, Char, byte, sbyte, int16, int, uint16, uint32, int64, uint64, single, double, enumeration, and one-dimensional 0-base array.
Can the previously customized countryattribute be applied to any target element? What if we want it to be applied only to the class type or method ?. NET framework certainly provides support in this aspect: system. attributeusageattribute class. Attributeusageattribute is a custom feature provided by. NET Framework. It mainly acts on other custom features to limit the target custom features. Let's take a look at its definition:
public sealed class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn); public bool AllowMultiple { get; set; } public bool Inherited { get; set; } public AttributeTargets ValidOn { get; } }
(1) This class only provides a public instance constructor. The parameter validon received by this class is the enumeration type attributetargets, which specifies the scope of the custom features, for example: assembly, module, structure, class, etc.
public enum AttributeTargets { Assembly = 1, Module = 2, Class = 4, Struct = 8, Enum = 16, Constructor = 32, Method = 64, Property = 128, Field = 256, Event = 512, Interface = 1024, Parameter = 2048, Delegate = 4096, ReturnValue = 8192, GenericParameter = 16384, All = 32767, }
(2) attributeusageattribute has an additional attribute allowmultiple, which indicates whether custom feature instances can be applied to the same target element multiple times.
(3) attributeusageattribute also has an additional attribute, inherited, which indicates whether to apply the custom feature to both the derived class and the rewritten Member when applied to the base class.
We modified the countryattribute class and defined two classes using the custom feature countryattribute. The following code:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.ReturnValue | AttributeTargets.Property, AllowMultiple = true, Inherited = true)] public class CountryAttribute : Attribute { public CountryAttribute() { } public CountryAttribute(string name) { this.Name = name; } public int PlayerCount { get; set; } public string Name { get; set; } } [Country("China")] [Country("America")] public class Sportsman { public string Name { get; set; } [Country(PlayerCount = 5)] public virtual void Play() { } } public class Hoopster : Sportsman { public override void Play() { } }
The countryattivity feature can only be used for classes, methods, method return values, and attributes:
Attributetargets. Class | attributetargets. Method | attributetargets. returnvalue | attributetargets. Property
In addition, instances of this custom feature can be applied to the same target element multiple times:
Allowmultiple = true
At the same time, this feature must be used not only for base classes, but also for derived classes and override members:
Inherited = true
In the sportsman class, we applied two custom features: [country ("China")] and [country ("America")]. We use custom features for the base class sportsman and its member methods play (). These custom features can also be obtained in its derived class hoopster. We will verify this in the following discussions.
If you are careful, you may find that [country ("China")] and [country (playercount = 5)] are somewhat similar but different. Why?
(1) countryattribute is used to define custom features, which can be abbreviated as country. When country is applied to a target element, the compiler confirms that country is a custom feature during compilation. Then, it searches for country in the attribute inheritance class. If country is not found, the suffix of attribute will be added to continue searching. If you cannot find it, an error will be reported!
(2) Country ("China") is passing the parameter "China" for the instance constructor. This is not a good explanation. The problem is [country (playercount = 5)], we didn't set the playercount parameter for the county constructor. Let's take a look at the Smart Tips When writing code in:
This special syntax identifies custom feature fields and attributes as named parameters. It allows custom feature objects to use named parameters to set common fields and attributes of the object after being constructed. This provides great flexibility. You can set the parameters of the Instance constructor to public fields or attributes, or set public fields and attributes to private, then, you can set parameters at the instance constructor. Of course, if you use an instance constructor, the parameters of this function must provide values. If you use public fields and attributes (named parameters), you can provide partial values, other fields and attributes can be maintained by default. We recommend that you use attributes instead of fields for more control.
Finally, let's take a look at what the compiler does for classes that use custom features?
Custom features America and China are written into metadata.
Section 2 apply attribute
If you only apply custom features to the target element, it does not seem to be of great significance. More importantly, after applying the features, we need to use the information that comes with these features. The feature information is usually used through reflection in combination with the static method of system. attribute. Let's take a look at the three important static methods of system. attribute:
Isdefined checks whether the specified target element applies the derived class of system. Attribute (custom feature). It has multiple overloading.
Getcustomattribute returns the property object that is applied to the target element of the same type as the specified type. If the target element does not have a feature instance, null is returned. If the target element has multiple instances of the specified feature type applied, an exception is thrown, and it also has multiple reloads.
Getcustomattributes returns the feature array that is applied to the target element. In its overload method, you can also specify the feature type, which also has multiple reloads.
We define a new custom feature:
[AttributeUsage(AttributeTargets.All)] public class TestAttribute : Attribute { }
Attributetargets. All indicates that this feature can be applied to any target element.
Modify the sportsman class:
[Country("China")] [Country("America")] public class Sportsman { public string Name { get; set; } [Country("Sports")] public virtual void Play() { Console.WriteLine("Play"); } }
[Country ("Sports")] is applied to method play (), indicating that the sport type is sports. Next we will transform the play () method of the hoopster class:
Public class hoopster: Sportsman {public override void play () {memberinfo [] members = This. getType (). getmembers (); foreach (memberinfo member in members) {attribute testattr = attribute. getcustomattribute (Member, typeof (testattribute); If (testattr! = NULL & testattr is testattribute) {console. writeline (testattrieline) testattr ). message);} If (attribute. isdefined (Member, typeof (countryattribute) {attribute [] attributes = attribute. getcustomattributes (member); foreach (attribute item in attributes) {countryattribute ATTR = item as countryattribute; If (ATTR! = NULL) {console. writeline (string. format ("sports type: {0} athletes: {1}", ATTR. name, ATTR. playercount ));}}}}}}
After obtaining all the members of the current object, the system loops through each member.
Neither the sportsman class nor the hoopster class applies the testattribute feature, so testattr remains null.
Then, the attribute. isdefined method is used to determine whether a custom countryattribute is applied to each member. If the feature is applied, obtain all the features applied to the member. Then the cyclic feature is used. If the feature is a custom feature of the countryattribute type, the defined sports type and number of athletes are printed. The running result is as follows:
Obviously, our definition of the basic class sportsman method play [country ("Sports")] has affected the subclass hoopster, which verifies the role of inherited = true we mentioned earlier. Since playercount is not assigned a value, it is still the default value 0.
Next, we will continue to transform the play method of the subclass hoopster:
[Country("Ball", PlayerCount = 5)] public override void Play() { //... }
Let's take a look at its running results:
This time not only sports/0 is printed, but also ball/5 is printed, because we applied [country ("ball ", playercount = 5)] feature. Method play not only obtains the feature information of the base class, but also has its own feature information.
Knot