C# Attribute
Attribute is a declarative tag used to convey behavior information about various elements (such as classes, methods, structs, enums, components, etc.) in a program at runtime. You can add declarative information to your program by using attributes. A declarative tag is described by placing square brackets ([ ]) in front of the element it applies to.
Attributes are used to add metadata, such as compiler directives and comments, descriptions, methods, classes, and other information. The .NET framework provides two types of attributes: predefined attributes and custom attributes.
Specifying Attributes
The syntax for specifying an attribute is as follows:
[attribute(positional_parameters, name_parameter = value, ...)]
element
The name and value of the attribute are specified within square brackets, placed before the element it applies to. Positional_parameters specify required information, while name_parameter specifies optional information.
Predefined Attributes
The .NET framework provides three predefined attributes:
- AttributeUsage
- Conditional
- Obsolete
AttributeUsage
The predefined attribute AttributeUsage describes how a custom attribute class can be used. It specifies the types of items to which the attribute can be applied.
The syntax for specifying this attribute is as follows:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
Where:
- The parameter validon specifies the language elements where the attribute can be placed. It is a combination of values from the AttributeTargets enumerator. The default value is AttributeTargets.All.
- The parameter allowmultiple (optional) provides a Boolean value for the AllowMultiple property. If true, the attribute is multi-use. The default value is false (single-use).
- The parameter inherited (optional) provides a Boolean value for the Inherited property. If true, the attribute can be inherited by derived classes. The default value is false (not inherited).
For example:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
Conditional
This predefined attribute marks a conditional method whose execution depends on a specified preprocessing identifier.
It causes conditional compilation of method calls based on the specified value, such as Debug or Trace. For example, displaying variable values when debugging code.
The syntax for specifying this attribute is as follows:
[Conditional(
conditionalSymbol
)]
For example:
[Conditional("DEBUG")]
The following example demonstrates this attribute:
Example
#define DEBUG
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}
class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}
When the above code is compiled and executed, it produces the following result:
In Main function.
In Function 1.
In Function 2.
Obsolete
This predefined attribute marks program entities that should not be used. It allows you to inform the compiler to discard a specific target element. For example, when a new method is used in a class but you still want to keep the old method in the class, you can mark it as obsolete by displaying a message that the new method should be used instead of the old one.
The syntax for specifying this attribute is as follows:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
Where:
The message parameter is a string that describes why the item is obsolete and what to use instead.
The iserror parameter is a boolean value. If it is true, the compiler should treat the use of the item as an error. The default value is false (the compiler generates a warning).
The following example demonstrates this attribute:
Example
using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
static void NewMethod()
{
Console.WriteLine("It is the new method");
}
public static void Main()
{
OldMethod();
}
}
When you attempt to compile this program, the compiler will give an error message stating:
Don't use OldMethod, use NewMethod instead
Creating Custom Attributes
The .NET Framework allows the creation of custom attributes that can store declarative information and be retrieved at runtime. This information can be related to any target element according to design standards and application needs.
Creating and using custom attributes involves four steps:
Declaring a custom attribute
Constructing a custom attribute
Applying a custom attribute to a target program element
Accessing attributes through reflection
The last step involves writing a simple program to read metadata to find various symbols. Metadata is data that describes other data and information. This program should use reflection to access attributes at runtime. We will discuss this in detail in the next chapter.
Declaring a Custom Attribute
A new custom attribute should derive from the System.Attribute class. For example:
// A custom attribute BugFix is assigned to classes and their members
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
In the above code, we have declared a custom attribute named DeBugInfo.
Constructing a Custom Attribute
Let's construct a custom attribute named DeBugInfo that will store information obtained from the debugging program. It will store the following information:
The bug's code number
The developer's name who identified the bug
The date of the last review of the code
A string message that stores the developer's marked message
Our DeBugInfo class will have three private properties to store the first three pieces of information and one public property to store the message. Therefore, the bug number, developer's name, and review date will be required positional parameters for the DeBugInfo class, and the message will be an optional named parameter.
Each attribute must have at least one constructor. The required positional parameters should be passed through the constructor. The following code demonstrates the DeBugInfo class:
Example
// A custom attribute BugFix is assigned to classes and their members
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get { return bugNo; }
}
public string Developer
{
get { return developer; }
}
public string LastReview
{
get { return lastReview; }
}
public string Message
{
get { return message; }
set { message = value; }
}
}
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
### Applying Custom Attributes
Apply the attribute just before its target:
## Example
```csharp
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// Member variables
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
In the next chapter, we will use Reflection class objects to retrieve this information. ```