C Sharp

Defining an Attribute Target

Now, looking again at the AttributeUsage attribute in the previous section, notice that the validon parameter is a positional-and, again, therefore mandatory-parameter. This parameter enables you to specify the types on which your attribute can be attached. Actually, the validon parameter in the AttributeUsage attribute is of the type AttributeTargets, which is an enumerationdefined as follows: -

public enum AttributeTargets
{
   Assembly    = 0x0001,
   Module      = 0x0002,
   Class       = 0x0004,
   Struct      = 0x0008,
   Enum        = 0x0010,
   Constructor = 0x0020,
   Method      = 0x0040,
   Property    = 0x0080,
   Field       = 0x0100,
   Event       = 0x0200,
   Interface   = 0x0400,
   Parameter   = 0x0800,
   Delegate    = 0x1000,
   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,
}

Notice when using the AttributeUsage attribute that you can specify AttributeTargets.All such that the attribute can be attached to any of the types listed in the AttributeTargets enumeration. This is the default if you don't specify the AttributeUsage attribute at all. Given that AttributeTargets.All is the default, you might be wondering why you would ever use the validon value. Well, named parameters can be used on this attribute, and you might want to change one of those. Remember that if you use a named parameter, you must precede it with all the positional parameters. This gives you an easy way to specify that you want the default attribute usage of AttriubuteTargets.All and still lets you set the named parameters.

So, when would you specify the validon (AttributeTargets)parameter and why? You use it when you want to control exactly how an attribute is being used. In the examples above, we created a RemoteObjectAttribute attribute that would be applicable for classes only, a TransactionableAttribute attribute that would apply only to methods, and a RegistryKeyAttribute attribute that would make sense with fields only. If we wanted to make sure that these attributes were used to annotate only the types for which they were designed, we could define them as follows (with attribute bodies left out for brevity): -

[AttributeUsage(AttributeTargets.Class)]
public class RemoteObjectAttribute : Attribute]
{
    ...
}
[AttributeUsage(AttributeTargets.Method)]
public class TransactionableAttribute : Attribute
{
    ...
}
[AttributeUsage(AttributeTargets.Field)]
public class RegistryKeyAttribute : Attribute
{
    ...
}

One last point regarding the AttributeTargets enumeration:you can combine members by using the | operator. If you have an attribute that would apply to both fields and properties, you would attach the AttributeUsage attribute to it as follows: -

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]