Vous êtes sur la page 1sur 10

In the .

NET Framework, attributes are used for many reasons - from defining which classes are
serializable to choosing which methods are exposed in a Web service. Attributes allow you to add
descriptions to classes, properties, and methods at design time that can then be examined at runtime
via reflection.
n .NET Framework, atributele sunt folosite pentru multe motive - de la definirea claselor care sunt serializable la
alegerea metodelor care sunt expuse de la un serviciu web. Atributele v permit s adugai descrieri pentru clase,
proprieti i metode de la timpul de proiectare care pot fi examinate la execuie prin reflexie.

Attributes are special classes that can be applied to classes, properties, and methods at design time.
Attributes provide a way to describe certain aspects of an element or determine the behavior of other
classes acting upon the element. Those descriptions and behaviors can then be accessed and
examined at runtime. You can think of attributes as a way of adding special modifiers to your class
members.
Atributele sunt clasele speciale care pot fi aplicate la clase, proprieti i metode de la timpul de proiectare. Atributele
ofer un mod de a descrie anumite aspecte ale unui element sau determina comportamentul de alte clase care
acioneaz asupra elementului. Aceste descrieri i comportamente pot fi accesate i examinate la runtime. V putei gndi
de atribute ca un mod de a aduga modificatori speciale pentru clasa dumneavoastra de membri.

For example, if you have written Web services, you are no doubt aware that the WebMethod attribute
must be applied to methods for them to be exposed through the service. This is a perfect example to
show the usage of attributes because the WebMethod attribute is used to extend the programming
model. There is no built-in way in C# of signifying that a method should be exposed through the Web
service (as there is, for example, of signifying that a method should be private), so the WebMethod
attribute was written to satisfy this need.

Developing custom attributes


The process of creating a custom attribute is very simple. There are just a few things you
must take into account before creating the attribute:

What is the purpose of the attribute?


Attributes can be used in any number of ways. You need to define what exactly the
attribute is meant to accomplish and make sure that specific functionality isn't already
covered by built-in .NET Framework assemblies. It is better to use first-class .NET
modifiers than attributes, as this simplifies the integration process with other
assemblies.

What information must the attribute store?


Is this attribute intended to be a simple flag to indicate a certain capability or will the
attribute have to store information? An attribute can hold a set of information given to it
at design time and expose that information at runtime. For an example, take a look at
the Alias attribute in the sample application.

In which assembly should the attribute reside?


In most cases, it is okay to include the attributes in the same assembly that will be

using them. However, there are instances when it is better to place the attributes inside
of a common, lightweight, shared assembly. This type of configuration allows clients to
use the attributes without referencing unneeded assemblies.

Which assemblies will recognize the attribute?


An attribute isn't worth anything if there are no modules that read it. You will most likely
place the classes that read the attribute inside of the same assembly in which the
attributes reside. However, as mentioned above, there are instances when you want
the logic that reads the attributes, and the attributes themselves, in different
assemblies.

Using attributes
Before we get into the details of how to create custom attributes, we need to look at how
they are used. For example, assume we have an attribute called "Hide" which effectively
hides properties so that they don't print to the screen. If we were to apply this attribute to
the "SSN" property, our code would look like Listing A.
Listing A
[Hide()]
publicstring SSN
{
get { return _ssn; }
set { _ssn = value; }
}
As a more complicated example, assume we have an attribute called "Alias". This
attribute's job is to determine the aliases a property may have. This allows the property's
value to be mapped into another property even if the property names don't match. This
attribute accepts a series of string values to hold as the mapping names (Listing B).
Listing B
[Alias("FirstName", "First")]
publicstring FName
{
get { return _fName; }
set { _fName = value; }
}
In this case, the property "FName" is mapped to both "FirstName" and "First." See the
example application for more detail on this type of usage.

Creating attributes
Creating attributes is a simple process. You define a class with the data you want to store,
and inherit from the System.Attribute class. Listing C is an example of how to create the
"Alias" attribute shown in the previous section.
Listing C
classAlias : System.Attribute
{
string[] _names;
public Alias(paramsstring[] names)
{
this.Names = names;
}
publicstring[] Names
{
get { return _names; }
set { _names = value; }
}
}
As you can see, this is just a normal class and, with the exception of inheriting from
System.Attribute, we didn't have to do anything special to enable it to be an attribute. We
simply defined the constructor that needed to be used and created a property and private
member to store the data.
Listing D is a much simpler attribute the "Hide" attribute. This attribute requires no
constructor (it uses the default) and doesn't store any data. This is because this attribute is
simply a "flag" type attribute:
Listing D
classHide : System.Attribute
{
//This is a simple attribute, that only requires
// the default constructor.
}

Reading attributes from code


Reading an attribute and examining its data is significantly more complicated than either
using an attribute or creating an attribute. Reading an attribute requires the developer to
have a basic understanding of how to use reflection on an object. If you are unfamiliar with
reflection, you may want to read my "Applied reflection" article series.

Let's assume that we are examining a class and we want to determine which properties
have the Alias attribute applied and which aliases are listed. Listing E implements this
logic:
Listing E
privateDictionary<string, string> GetAliasListing(Type destinationType)
{
//Get all the properties that are in the
// destination type.
PropertyInfo[] destinationProperties = destinationType.GetProperties();
Dictionary<string, string> aliases = newDictionary<string, string>();
foreach (PropertyInfo property in destinationProperties)
{
//Get the alias attributes.
object[] aliasAttributes =
property.GetCustomAttributes(typeof(Alias), true);
//Loop through the alias attributes and
// add them to the dictionary.
foreach (object attribute in aliasAttributes)
foreach (string name in ((Alias)attribute).Names)
aliases.Add(name, property.Name);
//We also need to add the property name
// as an alias.
aliases.Add(property.Name, property.Name);
}
return aliases;
}
The most important lines of this section of code are where we call GetCustomAttributes
and the section where we loop through the attributes and extract the aliases.
The GetCustomAttributes method is available from the PropertyInfo class that we
extracted from the object's Type. In the usage shown above, we tell the
GetCustomAttributes method what type of attribute we're looking for and also pass "true"
to enable it to pull inherited attributes. The GetCustomAttributes method returns an object
array if any matching attributes are found. There is also another overload of the method
that allows you to pull all attributes on the property, regardless of the attribute's type.
Once we have the attributes, we need to examine them and extract the information we're
looking for. This is done by looping through the objects in the array given to us from
GetCustomAttributes and casting each object into the type of attribute we're looking for.

After the object has been cast, we can access properties of the attribute the same way we
would access properties of any other class.
As I previously mentioned, reading the attribute is the hardest part. However, once you've
written the code to read an attribute, it is fairly easy to remember and implement in the
future.

In the sample application


I highly recommend that you download the sample application included with this
document. The sample application implements the following attributes and shows you how
to read/use them in a simple Windows application:

Alias This is the same Alias attribute mentioned above. This attribute is used
when you want to translate one type of object into another type of object. For example,
if you have a Customer object and an Address object, you could translate both of those
into a combined Person object, which contains the person's name and address. This
would be used when a direct cast can't be made.

DisplayName -- The sample application includes code that examines a class


instance and prints its property names and values to the screen. This attribute is used
to override what is sent to the screen for the property name. For example, a property
named "FName" could have a DisplayName attribute applied to it so that it displays as
"First Name".

Examine This attribute instructs the PrintObject() method in the sample


application to go a level deeper and print out the property values for the property that
has the Examine attribute applied. For example, the Customer object in the sample
application has the Examine attribute applied to the Address property. This instructs the
PrintObject method to print out all information in the address property.

Hide This simply instructs the PrintObject() method to not print the current
property out to the screen. This is used on the SSN property of the Customer object.

The sample application includes comments for every step it takes in implementing and
reading the attributes. Take a look and I'm sure you'll see some functionality you can take
advantage of in your own applications.

Attributes are a powerful feature in the C# programming language that can add metadata information
to your assemblies.
An attribute is actually an object that is associated with any of these elements: Assembly, Class,
Method, Delegate, Enum, Event, Field, Interface, Property and Struct. They can be used to associate

declarative information -- you can retrieve such information at runtime at a later point of time if need
be using reflection. In other words, you can use attributes to inject additional information to the
assemblies that can be queried at runtime if needed using reflection. An attribute comprises of its
name and optionally, a list of parameters. The attribute name corresponds to the attribute class.
You can take advantage of attributes to validate the business objects in your application. There are
two types of attributes -- intrinsic attributes and custom attributes. While the former is available as part
of the .Net framework, the latter can be implemented by deriving a class from the System.Attribute
class. The MSDN states: "An attribute is a piece of additional declarative information that is specified
for a declaration."
Let's now get into some code. The Obsolete attribute can be used to denote a method as obsolete -one that shouldn't be used anymore as it is no longer needed or may have some other alternative.
The following code snippet illustrates how you can use the Obsolete attribute on top of a method
declaration.
[Obsolete("This method is obsolete...")]
public static void DoSomeWork()
{
//Some code
}

If you use this method in your code and compile your program, you would see a warning displayed in
the output window of the Visual Studio IDE. So, you can ignore this warning if you want to. Now, what
if you want your developers no to use this method at all? Well, you can then use the second
parameter (it's optional though) while declaring the Obsolete attribute. Here's the modified version of
the DoSomeWork() method. Notice the usage of the Boolean parameter this time.
[Obsolete("This method is obsolete...", true)]
public static void DoSomeWork()
{
//Some code

When you pass "true" as the second optional parameter this time and compile your program, the code
wouldn't compile at all. That's what you wanted to do, isn't it?
Custom attributes
In this section we will explore how we can implement custom attributes. Custom attributes are classes
that inherit the System.Attribute class. So, to implement a custom attribute class, create a new class
and derive it from System.Attribute class as shown below.
using System;
public class CustomAttribute : Attribute
{
}

To control the usage of custom attributes, you can take advantage of the AttributeUsage class. This
class contains properties like, ValidOn, AllowMultiple and Inherited which can be used to control the
usage of your custom attribute.
The following snippet of code illustrates a modified version of our custom attribute class. Note the
usage of a constructor that accepts a string as an argument and assigns it to the private string
member of the class. This is just for illustration purposes only.
[AttributeUsage(AttributeTargets.All)]
public class CustomAttribute : Attribute
{
private string text;
public CustomAttribute(string text)
{
this.Text = text;

}
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
}

You can also specify the attribute targets that your custom attribute should be applied to. Here's how
you can do it.
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class CustomAttribute : Attribute
{

private string text;


public CustomAttribute(string text)
{
this.Text = text;
}
public string Text
{
get
{
return this.text;
}
set
{
this.text = value;
}
}
}

You can now use reflection to display all the attributes that are applied to a particular object using the
following code snippet.
MemberInfo memberInfo = typeof(CustomAttribute);
object[] attributes = memberInfo.GetCustomAttributes(true);
for (int i = 0, j = attributes.Length; i < j; i++)
{

Console.WriteLine(attributes[i]);
}

Now consider the following class on which we would apply our custom attribute.
[CustomAttribute("Hello World...")]
public class SomeClass
{
}

Note how the custom attribute has been used and a text passed as argument to it. The following code
snippet illustrates how you can print the content of the Text property.
MemberInfo memberInfo = typeof(SomeClass);
object[] attributes = memberInfo.GetCustomAttributes(true);
foreach (object attribute in attributes)
{
CustomAttribute customAttribute = attribute as CustomAttribute;
if (customAttribute != null)
Console.WriteLine("Text = {0}", customAttribute.Text);
else
Console.WriteLine();
}