Developing Custom Attribute
Contents
Defining Attribute
Now we will see how we can develop our own attributes. Here is a small recipe to create our own attributes. Derive our attribute class from System.Attribute class as stated in C# language specification (A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration) and we are done.
using System;
public class HelpAttribute : Attribute
{
}
Believe me or not we have just created a custom attribute. We can decorate our class with it as we did with obsolete attribute.
[Help()]
public class AnyClass
{
}
Note: it is a convention to use the word Attribute as a suffix in attribute class names. However, when we attach the attribute to a program entity, we are free not to include the Attribute suffix. The compiler first searches the attribute in System.Attribute derived classes. If no class is found, the compiler will add the word Attribute to the specified attribute name and search for it. But this attribute does nothing useful so far. To make it little useful let add something more in it.
using System;
public class HelpAttribute : Attribute
{
public HelpAttribute(String Descrition_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
[Help("this is a do-nothing class")]
public class AnyClass
{
}
In above example we have added a property to our attribute class which we will query at runtime in last section.
Defining or Controlling Usage of Our Attribute
AttributeUsage class is another pre-defined class that will help us in controlling the usage of our custom attributes. That is, we can define attributes of our own attribute class.
It describes how a custom attribute class can be used.
AttributeUsage has three properties which we can set while placing it on our custom attribute. The first property is:
ValidOn
Through this property, we can define the program entities on which our custom attribute can be placed. The set of all possible program entities on which an attribute can be placed is listed in the AttributeTargets enumerator. We can combine several AttributeTargets values using a bitwise OR operation.
AllowMultiple
This property marks whether our custom attribute can be placed more than once on the same program entity.
Inherited
We can control the inheritance rules of our attribute using this property. This property marks whether our attribute will be inherited by the class derived from the class on which we have placed it. Let's do something practical. We will place AttributeUsage attribute on own Help attribute and control the usage of our attribute with the help of it.
using System;
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false,
Inherited = false ]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
First look at AttributeTargets.Class. It states that Help attribute can be placed on a class only. This implies that following code will result in an error:
AnyClass.cs: Attribute 'Help' is not valid on this declaration type.
It is valid on 'class' declarations only. Now try to put in on method
[Help("this is a do-nothing class")]
public class AnyClass
{
[Help("this is a do-nothing method")] //error
public void AnyMethod()
{
}
}
We can use AttributeTargets.All to allow Help attribute to be placed on any program entity. Possible values are:
- 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 )
Now consider AllowMultiple = false. This state that attribute cannot be placed multiple time.
[Help("this is a do-nothing class")]
[Help("it contains a do-nothing method")]
public class AnyClass
{
[Help("this is a do-nothing method")] //error
public void AnyMethod()
{
}
}
It generates a compile-time error.
AnyClass.cs: Duplicate 'Help' attribute
Ok now discuss the last property. Inherited, indicates whether the attribute, when placed on a base class, is also inherited by classes that derive from that base class. If Inherited for an attribute class is true, then that attribute is inherited. However if Inherited for an attribute class is false or it is unspecified, then that attribute is not inherited. Let suppose we have following class relationship.
[Help("BaseClass")]
public class Base
{
}
public class Derive : Base
{
}
We have four possible combinations:
- [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 Query (we will see later how to Query attributes of a class at run-time) Derive Class for Help attribute, we will not found it as the inherited attribute is set to false.
Second Case
Second case is no different as inherited attribute is set to false in this case also.
Third Case
To explain the third and fourth cases, let's add the same attribute to the derive class also.
[Help("BaseClass")]
public class Base
{
}
[Help("DeriveClass")]
public class Derive : Base
{
}
Now if we Query about Help attribute, we will get the drive class attribute only as inherited is true but multiples are not allowed so the base class Help is overridden by the Derive class Help attribute.
Fourth Case
In fourth case we will get both attributes when we query our Derive class for Help attribute as both inheritance and multiples are allowed in this case. Note: AttributeUsage attribute is only valid on classes derived from System.Attribute and both AllowMultiple and Inherited are false for this attribute.