Wednesday, May 27, 2009

Attributes in C#.Net

Attributes are a new kind of declarative information. We can use attributes to define both design-level information (such as help file, URL for documentation) and run-time information (such as associating XML field with class field). We can also create "self-describing" components using attributes.

"An attribute is a piece of additional declarative information that is specified for a declaration."
- MSDN

Attributes
C# allows you to add declarative information to a program in the form of an attribute. An attribute defines additional information (metadata) that is associated with a class, structure, method, and so on. For example, you might define an attribute that determines the type of button that a class will display. Attributes are specified between square brackets, preceding the item to which they apply. Thus, an attribute is not a member of a class. Rather, an attribute specifies supplemental information that is attached to an item.
When do we need attributes ?

The advantage of using attributes resides in the fact that the information that it contains is inserted into the assembly. This information can then be consumed at various times for all sorts of purposes:
1. An attribute can be consumed by the compiler. The System.ObsoleteAttribute attribute that we have just described is a good example of how an attribute is used by the compiler, certain standard attributes which are only destined for the compiler are not stored in the assembly. For example, the SerializationAttribute attribute does not directly mark a type but rather tells the compiler that type can be serialized. Consequently, the compiler sets certain flags on the concerned type which will be consumed by the CLR during execution such attributes are also named pseudo-attributes.
2. An attribute can be consumed by the CLR during execution. For example the .NET Framework offers the System.ThreadStaticAttribute attribute. When a static field is marked with this attribute the CLR makes sure that during the execution, there is only one version of this field per thread.
3. An attribute can be consumed by a debugger during execution. Hence, the System.Diagnostics.DebuggerDisplayAttribute attribute allows personalizing the display of an element of the code(the state of an object for example) during debugging.
4. An attribute can be consumed by a tool, for example, the .NET framework offers the System.Runtime.InteropServices.ComVisibleAttribute attribute. When a class is marked with this attribute, the tlbexp.exe tool generates a file which will allow this class to be consumed as if it was a COM object.
5. An attribute can be consumed by your own code during execution by using the reflection mechanism to access the information. For example, it can be interesting to use such attributes to validate the value of fields in your classes. Such a field must be within a certain range. Another reference field must not be null. A string field can be atmost 100 character. Because of the reflection mechanism, it is easy to write code to validate the state of any marked fields. A little later, we will show you such an example where you can consume attributes by your own code.
6. An attribute can be consumed by a user which analyses an assembly with a tool such as ildasm.exe or Reflector. Hence you could imagine an attribute which would associate a character string to an element of your code. This string being contained in the assembly, it is then possible to consult these comments without needing to access source code.
Things to know about attributes
1. An attribute must be defined by a class which derives from System.Attribute.
2. An Instance of the attribute class is only instantiated when the reflection mechanism accesses one of its representatives, Depending on its use, an attribute class in not necessarily instantiated(as with the System.ObsoleteAttribute class which does not need to be used by the reflection mechanism).
3. The .NET framework puts several attributes to your disposition. Certain attributes are destined to be used by the CLR. Other are consumed by the compiler or tools supplied by Microsoft.
4. You have the possibility of creating your own attribute classes, They will then necessarily be consumed by your program as you cannot tinker the compiler or the CLR.
5. By convention, the name of an attribute class is suffixed by Attribute. However, an attribute named XXXAttribute can be used in c# both using the XXXAttribute expression but also with the XXX expression when it marks an element of the code.
Reference:
Practical .NET2 and C#2 By Patrick Smacchia


Attribute Basics

An attribute is supported by a class that inherits System.Attribute. Thus, all attribute classes must be subclasses of Attribute. Although Attribute defines substantial functionality, this functionality is not always needed when working with attributes. By convention, attribute classes often use the suffix Attribute. For example, ErrorAttribute would be a name for an attribute class that described an error.
When an attribute class is declared, it is preceded by an attribute called AttributeUsage. This built-in attribute specifies the types of items to which the attribute can be applied. Thus, the usage of an attribute can be restricted to methods, for example.
Creating an Attribute
In an attribute class, you will define the members that support the attribute. Often attribute classes are quite simple, containing just a small number of fields or properties. For example, an attribute might define a Note that describes the item to which the attribute is being attached. Such an attribute might look like this:
[AttributeUsage(AttributeTargets.All)]
public class NoteAttribute : Attribute {
string strNote;

public NoteAttribute(string comment) {
strNote = comment;
}

public string Note {
get {
return strNote;
}
}
}
Let’s look at this class, line by line.
The name of this attribute is NoteAttribute. Its declaration is preceded by the AttributeUsage attribute, which specifies that NoteAttribute can be applied to all types of items. Using AttributeUsage, it is possible to narrow the list of items to which an attribute can be attached, and we will examine its capabilities later in this chapter.
Next, NoteAttribute is declared and it inherits Attribute. Inside NoteAttribute there is one private field, strNote, which supports one public, read-only property: Note. This property holds the description that will be associated with the attribute. There is one public constructor that takes a string argument and assigns it to Note.
At this point, no other steps are needed, and NoteAttribute is ready for use.
Attaching an Attribute
Once you have defined an attribute class, you can attach the attribute to an item. An attribute precedes the item to which it is attached and is specified by enclosing its constructor inside square brackets. For example, here is how NoteAttribute can be associated with a class:
[NoteAttribute("This class uses an attribute.")]
class UseAttrib {
// ...
}
This constructs a NoteAttribute that contains the comment “This class uses an attribute.” This attribute is then associated with UseAttrib.
When attaching an attribute, it is not actually necessary to specify the “Attribute” suffix. For example, the preceding class could be declared this way:
[Note("This class uses an attribute.")]
class UseAttrib {
// ...
}
Here, only the name Note is used. Although the short form is correct, it is usually refer to use the full name when attaching attributes, because it avoids possible confusion and ambiguity.
Obtaining an Object’s Attributes
Once an attribute has been attached to an item, other parts of the program can retrieve the attribute. To retrieve an attribute, you will usually use one of two methods. The first is GetCustomAttributes( ), which is defined by MemberInfo and inherited by Type. It retrieves a list of all attributes attached to an item. Here is one of its forms:
object[ ] GetCustomAttributes(bool searchBases)
If searchBases is true, then the attributes of all base classes through the inheritance chain will be included. Otherwise, only those classes defined by the specified type will be found.
The second method is GetCustomAttribute( ), which is defined by Attribute. One of its forms is shown here:
static Attribute GetCustomAttribute(MemberInfo mi, Type attribtype)
Here, mi is a MemberInfo object that describes the item for which the attributes are being obtained. The attribute desired is specified by attribtype. You will use this method when you know the name of the attribute you want to obtain, which is often the case. For example, assuming that the UseAttrib class has the NoteAttribute, to obtain a reference to the NoteAttribute, you can use a sequence like this:
// Get a MemberInfo instance associated with a
// class that has the NoteAttribute.
Type t = typeof(UseAttrib);

// Retrieve the NoteAttribute.
Type tRemAtt = typeof(NoteAttribute);
NoteAttribute ra = (NoteAttribute)
Attribute.GetCustomAttribute(t, tRemAtt);
This sequence works because MemberInfo is a base class of Type. Thus, t is a MemberInfo instance.
Once you have a reference to an attribute, you can access its members. Thus, information associated with an attribute is available to a program that uses an element to which an attribute is attached. For example, the following statement displays the Note field:
Console.WriteLine(ra.Note);
The following program puts together all of the pieces and demonstrates the use of NoteAttribute:
// A simple attribute example.

using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.All)]
public class NoteAttribute : Attribute {
string strNote;
public NoteAttribute(string comment) {
strNote = comment;
}

public string Note {
get {
return strNote;
}
}
}

[NoteAttribute("This class uses an attribute.")]
class UseAttrib {
// ...
}

class AttribDemo {
public static void Main() {
Type t = typeof(UseAttrib);

Console.Write("Attributes in " + t.Name + ": ");

object[] attribs = t.GetCustomAttributes(false);
foreach(object o in attribs) {
Console.WriteLine(o);
}

Console.Write("Note: ");

// Retrieve the NoteAttribute.
Type tRemAtt = typeof(NoteAttribute);
NoteAttribute ra = (NoteAttribute)
Attribute.GetCustomAttribute(t, tRemAtt);

Console.WriteLine(ra.Note);
}
}
The output from the program is shown here:
Attributes in UseAttrib: NoteAttribute
Note: This class uses an attribute.

1 comment:

Locations of visitors to this page