.NET Development Foundation/Attributes

Reflexion: Building custom attributes

Annexes: Building custom attributes
Original text for this page authored by William "Scott" Baker

Attributes Summary
Attributes are a way to "tag" elements of your code's metadata with descriptive information that can be accessed at runtime using reflection. Attributes must derive from System.Attribute, either directly or indirectly. A multitude of attributes exist in the .NET framework; you can also define your own. There are three aspects to using attributes in your code:
 * 1) Defining a custom attribute class, which involves:
 * 2) Assigning the AttributeUsageAttribute attribute to your class.
 * 3) Writing the code to define your custom attribute class.
 * 4) Creating the parameters for your class.
 * 5) Assigning an attribute to a code member.
 * 6) Retrieving attribute information at runtime.

Creating a Custom Attribute Class
As previously mentioned, there are several predefined attributes in the .NET Framework; you may have already used them in your code. The XML parser in particular relies heavily on attributes when (de)serializing objects. You can also define your own custom attributes, as we will show here. Defining a custom attribute involves three steps:
 * 1) Assigning the AttributeUsageAttribute attribute to your class.
 * 2) Writing the code to define your custom attribute class.
 * 3) Creating the parameters for your class.

Assigning the "AttributeUsageAttribute" To Your Class
The "scope" and other characteristics of your attribute should be specified by the use of the AttributeUsageAttribute attribute. Note: In Visual Basic, use of the AttributeUsageAttribute attribute is required on all custom attributes. Applying the AttributeUsageAttribute attribute to a class: [ AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)] public Class QualityCheckAttribute : System.Attribute {  // ...  } Notice the use of " AttributeUsage " vs. "AttributeUsageAttribute". By convention, all attributes are named with the "<tt>Attribute</tt>" suffix - but the suffix can be omitted when used in code. This holds true for user defined attributes as well; the <tt>QualityCheckAttribute</tt> attribute could be referenced as either: [QualityCheck] // or... [QualityCheckAttribute] The <tt>AttributeUsageAttribute</tt> has three members: <tt>ValidOn, AllowMultiple</tt> and <tt>Inherited</tt>.

All (any element)  Delegate    GenericParameter    Parameter Assembly           Enum        Interface           Property Class              Event       Method              ReturnValue Constructor        Field       Module*             Struct *Module refers to a portable executable (.exe or .dll), and not a Visual Basic standard module. You can also combine target types as a bitwise OR operation to specify multiple acceptable values: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [QualityCheck("Scott Baker", "02/28/06", IsApproved = true,   Comment = "This code follows all established guidelines.  Release approved.")] [QualityCheck("Matt Kauffman", "01/15/06", IsApproved = false,   Comment = "Code quality much improved. Minor revision required.")] [QualityCheck("Joe Schmoe", 01/01/06", IsApproved = false,   Comment = "This code is a mess and needs a complete rewrite")] public class MyClass { // ...  } [AttributeUsage(AttributeTargets.Class)] public class AttrOneAttribute : Attribute {   // ...  } // This attribute will not be inherited  [AttributeUsage(AttributeTargets.Class, Inherited = false )] public class AttrTwoAttribute : Attribute {   // ...  } [AttrOne] [AttrTwo] public class ClassOne  {   // ...  } // This class inherits AttrOne from ClassOne,  // but not AttrTwo  public class ClassTwo : ClassOne {   // ...  }
 * The <tt>ValidOn</tt> member accepts <tt>AttributeTargets</tt> enum values, and restricts your attribute to the code types you specify. The default value is <tt>AttributeTargets.All</tt>. You can confine your attribute to classes, enums, return values or any of the list below:
 * <tt>AllowMultiple</tt> is a boolean value that determines whether or not an attribute can be applied more than once to a given member. The default value is <tt>false</tt>.  The example below illustrates multiple instances of the same attribute on an element of code:
 * The <tt>Inherited</tt> member determines whether attributes set on a class will be inherited by classes further down the inheritance tree. The default value is true:

Defining A Custom Attribute Class
public Class QualityCheckAttribute : System.Attribute // direct {  // ...  } public Class FinalCheck : QualityCheckAttribute  // indirect { // ... } [AttributeUsage(AllowMultiple = true, Inherited = false)] public Class QualityCheckAttribute : System.Attribute {  // ...  } As previously mentioned, the use of <tt>AttributeUsageAttribute</tt> is required in VB.  In C#, it is automatically applied with the default values if not declared.
 * Attributes are classes that inherit from <tt>System.Attribute</tt>, either directly or indirectly:
 * Attribute classes have the <tt>AttributeUsageAttribute</tt> attribute:

Creating Parameters For An Attribute
Attributes accept two types of parameters: positional and named. Positional parameters are defined by public constructors in your class, must be in the order defined and are required; named parameters are defined by public properties, can be in any order and are optional.

Positional Parameters
Positional parameters for an attribute are defined by the constructors in the class. As with any class, constructors can be overloaded and a default constructor that takes no parameters can be defined. As with any other class, the signature of the positional parameters must match the signature of a constructor in the attribute class: public class QualityCheckAttribute : Attribute {  public QualityCheckAttribute(string Name, string Date) // ... }

Named Parameters
Named parameters for an attribute are defined by public properties defined in the class. Named parameters are optional and are declared after any positional parameters. The <tt>IsApproved</tt> property demonstrates a named parameter: public class QualityCheckAttribute : Attribute {  private string _name; private string _date; private bool isApproved; public bool IsApproved {    get {return isApproved;} set {isApproved = value;} }   public QualityCheckAttribute(string Name, string Date) {    // ...    } }

Keep in mind that a variable in your code can be both a positional parameter and a named parameter. If we were to add public properties for the  and   fields we could use them either as named parameters or positional parameters. Of course, this is not a recommended practice: required parameters should be positional and optional parameters should be named.

Assigning An Attribute To A Code Member
You have already seen examples of assigning an attribute to a code member. However, there are some points that must be clarified.
 * Disambiguation is the clarification of the use of an attribute on a code member.
 * Syntax - there is more than one way to apply multiple attributes.

Disambiguation
The code below isn't clear as to whether <tt>SomeAttribute</tt> applies to the method <tt>MyMethod</tt> or to its <tt>return</tt> value: public class MyAttribute : Attribute {  [SomeAttribute("Hello")] public string MyMethod(aString) {    return aString; } } Disambiguation resolves these issues. By specifying the code type the attribute is applied to, we are able to resolve the confusion. The code below shows that the attribute applies to the <tt>return</tt> value: public class MyAttribute : Attribute {  [ return : SomeAttribute] public string MyMethod(aString) {    return aString; } } The table below lists all declarations where attributes are allowed; for each declaration, the possible targets for attributes on the declaration are listed in the second column. Targets in bold are the defaults. Declaration              Possible targets assembly                 assembly module                   module class                    type struct                   type interface                type enum                     type delegate                 type, return method                   method, return parameter                param field                    field property — indexer       property property — get accessor  method, return property — set accessor  method, param, return event — field            event, field, method event — property         event, property event — add              method, param event — remove           method, param *Reference: Disambiguating Attribute Targets (C# Programming Guide) One would think that the <tt>AttributeUsageAttribute's AttributeTargets</tt> in an attribute's definition would help to prevent this confusion: one would be wrong. The compiler does not use the <tt>AttributeUsageAttribute</tt> information when resolving conflicts. Even if you define an attribute to apply only to a specific type, for instance <tt>AttributeTargets.Return</tt>, you must still clarify that it applies to the <tt>return</tt> type when applying the attribute or the compiler will use the default target <tt>method</tt> type, and throw an error.

Syntax: Applying Multiple Attributes
When applying more than one attribute to a member, there are two ways to do so: [AttrOne(...), AttrTwo(...)] // or... [AttrOne(...)] [AttrTwo(...)] The two are equivalent. Keep in mind that if you are going to specify more than one attribute in a single brace, they must apply to the same target type. If not, you must give each type a separate declaration: [return : AttrOne(...), method : AttrTwo(...)] // <-- invalid! // instead, you must... [return : AttrOne(...)] [method : AttrTwo(...)]

Retrieving Attribute Information At Runtime
Being able to declare and apply attributes is not very helpful unless we can retrieve that data and do something with it. Fortunately, its a straightforward process. There are three basic scenarios that will be addressed:
 * 1) Retrieve a single attribute from a member.
 * 2) Retrieve multiple attributes from a member.
 * 3) Retrieve attributes of a single type from multiple members.

Retrieving A Single Attribute From A Member
To access attribute information: The example code below declares a class <tt>ExampleClass</tt> with a <tt>QualityCheck</tt> attribute. The <tt>GetSingleAttribute</tt> method accepts a target member type and the type of attribute you're looking for. The <tt>Attribute.GetCustomAttribute</tt> method retrieves the attribute information into the <tt>attr</tt> object, from which we can read the all-important <tt>IsApproved</tt> property: [QualityCheck("Scott Baker", "02/04/2006", IsApproved = false)] public class ExampleClass {  public static void Main {    GetSingleAttribute(typeof(ExampleClass), typeof(QualityCheck)) }  public static void GetSingleAttribute(Type targetType, Type attrType) {    typeof(attrType) attr = (attrType)Attribute. GetCustomAttribute (targetType, typeof(attrType)); if (attr == null) { //... }    else {      Console.Writeline(attr.IsApproved); }  }
 * 1) Declare an instance of the attribute type.
 * 2) Use the <tt>Attribute.GetCustomAttribute(type, typeof)</tt> method to read the attribute into the instance.
 * 3) Use the properties of the instance to read the values.

An important factor to keep in mind is that the <tt>GetCustomAttribute</tt> method is designed to read one and only one attribute. <tt>GetCustomAttribute</tt> actually checks to see if more than one attribute matches - if there is no match it returns <tt>null</tt>, but if there is more than one match it will throw an <tt>AmbiguousMatchException</tt>. When checking for attributes the only time it is safe to use <tt>GetCustomAttribute</tt> is when the attribute's definition states <tt>[AttributeUsage(AllowMultiple= false )]</tt>.

Retrieving Multiple Attributes From A Member
Reading multiple instances of an attribute on a member isn't much different than reading one; to read multiple attributes use the plural <tt>GetCustomAttributes</tt>, which returns an array of attributes. You can then iterate through the resultant array and read the values:

QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(t, typeof(QualityCheck)); foreach (QualityCheck attr in attrArray) {  Console.Writeline(attr.IsApproved); }

Retrieving A Single Attribute Type From Multiple Members
What if you want to do something a little more complex, like checking each method in a class for a QualityCheck attribute? Thanks to the <tt>System.Reflection</tt> namespace, we don't have to even break a sweat. Simply read all the members (methods, in this instance) into a <tt>MemberInfo</tt> array and iterate through them: using System.Reflection public class ExampleClass {  public static void Main {    RetrieveAttributes(typeof(ExampleClass)); }  public void RetrieveAttributes(Type t)   { MemberInfo[] methodList = t.GetMethods; foreach (MemberInfo m in methodList) {      QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(m, typeof(QualityCheck)); foreach (QualityCheck attr in attrArray) {        Console.Writeline(attr.IsApproved); }    }   } }