Common Type System—Custom Attributes
Custom Attributes
We’ve seen throughout this chapter various IL keywords that compilers use to modify runtime behavior for the type or member they have been applied to. These are pseudo-custom attributes, which are serialized using a fixed size and location in the metadata and usually have their own efficient storage slots in CLR data structures. The CLR intimately knows about these attributes, will notice them, and will respond to them accordingly, based on the documented contract for the specific attribute.
However, the CLR also permits users to attach custom attributes to CLR data types. This is done by creating
a new type that derives from System.Attribute, and providing a set of fields and properties the
attribute will carry around when instantiated at runtime. A user of your attribute may then attach an
instance to an assembly, module, type, or member. The attribute and its instantiation information are
serialized into the assembly’s metadata and can be rehydrated at runtime. User components may then
inspect instances at runtime for the presence of these attributes and react accordingly, much like the runtime
does with pseudo-custom attributes.
Here is an example attribute type in C#:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] class MyAttribute : System.Attribute { private string myString; private int mySize; public MyAttribute(string myName) { this.myName = myName; this.mySize = 8; // default } public string MyName { get { /*...*/ } } public int MySize { get { /*...*/ } set { /*...*/ } } }
A user may then attach this to a class or method (the two legal targets based on AttributeUsage, which
itself is an attribute). This is done in C# with the [ Attribute ] syntax and VB with the < Attribute
> syntax, for example:
[MyAttribute("MyFoo")] class Foo { [MyAttribute("MyFoo::MyBar", MySize = 16)] public void Bar() { } }
Notice that C#’s attribute syntax permits you to call the constructor and supply a set of property values.
The System.Reflection.MemberInfo type and all of its derived types (Type, MethodInfo,
PropertyInfo, FieldInfo, and so forth) then enable components to read a type system component’s
attributes using the GetCustomAttributes method. This small snippet of code reads Foo and
Foo.Bar’s attributes:
Type fooType = typeof(Foo); object[] myAttrOnType = fooType.GetCustomAttributes( typeof(MyAttribute), false); if (myAttrOnType.Length > 0) { // Do something special...it has a MyAttribute. } MethodInfo fooBarMethod = fooType.GetMethod("Bar"); foreach (object attr in fooBarMethod.GetCustomAttributes(false)) { if (attr.GetType() == typeof(MyAttribute)) { // Has a MyAttribute, do something about it. } }
This was a very quick overview. Please refer to Chapter 14, which discusses more about these APIs, the internals of custom attributes, such as their storage format, and more about how they relate to dynamic programming.
|

