Manipulation des attributs en dotnet

Lainé Vincent

À travers cet article nous allons découvrir le système des attributs de dotnet, voir à quoi ils servent, et comment créer ses propres attributs.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Manipulation des attributs en dotnet

- Auteur : Lainé Vincent

- Parution : 17 octobre 2006

- Licence : Copyright ® 2006 Lainé Vincent. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Les attributs ? Ké za ko ?

- I-A. C'est quoi ?

- I-B. À quoi ça sert ?

- II. Utiliser les attributs

- III. Comment créer ses propres attributs ?

- III-A. Définition de la portée d'un attribut

- III-B. L'héritage des attributs

- III-C. Instance multiple des attributs

- IV. Cas concret d'utilisation des attributs personnalisés

- IV-A. La classe à paramétrer

- IV-B. La classe d'attribut personnalisé

- IV-C. Utilisation de notre attribut personnalisé

- IV-D. La classe de gestion de l'affichage des propriétés

- V. Conclusion

- VI. Téléchargement

I - Les attributs ? Ké za ko ?

I-A - C'est quoi ?

Un attribut est un petit morceau de code permettant de paramétrer un assembly, une classe, un constructeur, un délégué, une énumération, un événement, un champ, une interface, une méthode, un paramètre, une propriété, une valeur de retour, une structure ou un autre attribut, etc.
Ils ont une syntaxe particulière et ne peuvent pas être confondus avec d'autres instructions du langage.

Une classe avec un attribut personnel

[MyAttribut(true)]
public class A
{

}

I-B - À quoi ça sert ?

Les attributs servent donc à paramétrer une classe ou autre, afin que les appelants puissent facilement modifier leurs comportements en fonctions de ces paramètres.
Concrètement nous retrouvons les attributs dans beaucoup de classes de l'espace de nom System.Windows.Forms. Les attributs servent par exemple à définir l'image associée à un composant dans les boites à outils de nos EDI préférés.
Un autre attribut que nous rencontrons assez souvent est l'attribut Serializable. Dans ce cas l'attribut sert à « marquer » une classe comme étant sérialisable par le framework.

II - Utiliser les attributs

Maintenant que nous savons ce qu'est un attribut, voyons comment nous pouvons les exploiter dans nos programmes. L'utilisation des attributs est étroitement liée à la réflexion.
En effet c'est grâce à la réflexion que vous allez pouvoir déterminer si une classe, une propriété, un membre possèdent un ou plusieurs attributs.

Obtenir les attributs d'une classe

MyClass myClass = new MyClass();

Type cType = myClass.GetType();
object[] atts = cType.GetCustomAttributes(false); //On obtient les attributs de la classe sans prendre en compte l'héritage

for(int i = 0; i  atts.Length; i++ )
{
if( atts[i] is CustomAttribut )
{
Console.WriteLine( ((CustomAttribut)atts[i]).MyValue );
}
}

III - Comment créer ses propres attributs ?

Rien n'est plus simple que de créer ces propres attributs. ;-)
Pour cela il suffit de créer une classe dérivant de la classe Attribute de faire terminer le nom de sa classe par Attribute.

Création d'un attribut personnalisé

public class CustomAttribute : Attribute
{
private string myValue;

public CustomAttribute(string myValue)
{
this.myValue = myValue;
}

public string MyValue
{
get
{
return this.myValue;
}
}
}

Cet attribut s'utilise ainsi :

Utilisation du CustomAttribut

[Custom("une valeur")]
public class UneClasse
{

}

La valeur définie dans l'attribut lors de son utilisation est obligatoire à cause du constructeur défini la classe CustomAttribute.

III-A - Définition de la portée d'un attribut

Les attributs peuvent être conçu de façon a ce qu'il ne soit valable que sur un certain type de structure de code.
Ainsi un attribut peut être dédié spécialement aux propriétés, aux classes, aux méthodes, etc.
Afin de rendre un attribut spécialisé pour une structure de code, il suffit de lui attribuer un … attribut.

[AttributeUsage(AttributeTargets.Property)]
public class CustomAttribut
{
private string myValue;

public CustomAttribute(string myValue)
{
this.myValue = myValue;
}

public string MyValue
{
get
{
return this.myValue;
}
}
}

L'énumération AttributeTargets peut prendre une multitude de valeurs. Je vous invite à voir la msdn afin de connaître les possibilités.
Liste des énumérations de l'énumération AttributeTargets
Comment faire pour définir un attribut pouvant être utilisé à la fois sur une propriété et une méthode par exemple ? Il suffit de faire une addition binaire entre toutes les valeurs souhaitées.

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

III-B - L'héritage des attributs

Une chose à laquelle on ne pense pas forcément c'est le problème de l'héritage. En effet que ce passe-t-il quand nous créons une classe qui hérite d'une autre qui définie par exemple des propriétés paramétrées avec des attributs ?
Eh bien là aussi le framework nous permet de personnaliser le comportement de l'héritage des attributs.

/// <summary>
/// Classe définissant l'attribut personnalisé
/// </summary>
[AttributeUsage(AttributeTargets.Property, Inherited=true)]
public class DisplayAttribute : Attribute
{
private bool display = false;

/// <summary>
/// Constructeur par défaut
/// </summary>
public DisplayAttribute(bool display)
{
this.display = display;
}

/// <summary>
/// Obtient ou défini une valeur indiquant si la propriété suivi a été modifié
/// </summary>
public bool Display
{
get
{
return this.display;
}
}
}

Le paramètre nommé Inherited permet de définir si l'attribut peut affecter les membres hérités.
Exemple :

public class Information
{

[Display(true)]
public string Name
{
get
{
return this.name;
}

set
{
this.name = value;
}
}
}

public class InformationsSpecifiques : Information
{

}

Dans cet exemple la classe InformationsSpecifiques hérite de la classe Information qui défini l'attribut Display à vrai pour la propriété Name. Que ce passe-t-il lorsqu'un objet de type InformationsSpécifique est examiné pour en extraire les attributs ? Dans notre cas la propriété Name définie par la classe Information va être examineé et l'attribut Display va être détecté, car nous avons défini le paramètre Inherited à true.
Au niveau du code de la détection y a-t-il un moyen d'obtenir les attributs dont la propriété Inherited à false ?
Contrairement à toute attente oui … En effet le fait de paramétrer un attribut avec Inherited=false, n'empêche pas les « inspecteurs » d'obtenir la valeur de l'attribut avec le code suivant :

pi.GetCustomAttributes(typeof(DisplayAttribute), true);

En effet le fait de positionner le second argument de GetCustumAttributes (inherited) à « vrai » permet d'obtenir les attributs hérités.

III-C - Instance multiple des attributs

Une autre question qui se pose est : « Est-il possible d'avoir plusieurs fois le même attribut sur le même élément ? »
Là encore la réponse nous est donnée par un paramètre nommé : AllowMultiple. Si ce paramètre est défini à true vous pourrez mettre deux fois l'attribut, sinon non.

public class Information
{

[Author("dev01")]
[Author("author")]
public string Name
{
get
{
return this.name;
}

set
{
this.name = value;
}
}
}

IV - Cas concret d'utilisation des attributs personnalisés

Tout ceci est un peu trop théorique. C'est pourquoi nous allons voir un exemple d'utilisation des attributs personnalisés à travers un système permettant de paramétrer l'affichage des propriétés.

IV-A - La classe à paramétrer

La classe ci-dessous sera la classe dont nous allons paramétrer l'affichage.

public class Informations
{
private string name;
private string firstName;
private string mail;

public Informations()
{

}

public string Name
{
get
{
return this.name;
}

set
{
this.name = value;
}
}
}

IV-B - La classe d'attribut personnalisé

Cette classe définit notre attribut qui nous permettra de savoir si les informations de la classe doivent être visibles ou non.

[AttributeUsage(AttributeTargets.Property, Inherited=true, AllowMultiple = false)]
public class DisplayAttribute : Attribute
{
private bool display;

public DisplayAttribute(bool display)
{
this.display = display;
}

public bool Display
{
get
{
return this.display;
}

set
{
this.display = value;
}
}
}

L'attribut que nous avons défini prend un paramètre dans le constructeur afin de définir si la propriété doit être affichée ou non.
La propriété qui nous intéresse ici est Display.

IV-C - Utilisation de notre attribut personnalisé

Voyons maintenant comment nous allons nous servir de notre attribut.

public class Informations
{
private string name;
private string firstName;
private string mail;

public Informations()
{

}

[Display(true)]
public string Name
{
get
{
return this.name;
}

set
{
this.
this.name = value;
}
}

[Display(true)]
public string FirstName
{
get
{
return this.firstName;
}

set
{
this.firstName = value;
}
}

public string Mail
{
get
{
return this.mail;
}

set
{
this.mail = value;
}
}

}

L'utilisation de notre attribut dans la classe à paramétrer est simple.
Il nous suffit de le définir à true pour dire que nous voulons que la propriété soit affichée.

IV-D - La classe de gestion de l'affichage des propriétés

Cette classe permet d'afficher ou non une propriété en fonction de l'attribut Display.

private void UpdateInformations()
{
this.listViewInformations.Clear();
this.listViewInformations.Columns.Clear();

if( this.informations != null )
{
PropertyInfo[] pis = this.informations.GetType().GetProperties();
foreach (PropertyInfo pi in pis)
{
foreach (object o in pi.GetCustomAttributes(typeof(DisplayAttribute), false))
{
if (((DisplayAttribute)o).Display)
{
this.listViewInformations.Columns.Add(pi.Name);
}
}
}

List<string> val = new List<string>();
for (int i = 0; i < this.listViewInformations.Columns.Count; i++)
{
val.Add(this.informations.GetType().GetProperty(
this.listViewInformations.Columns[i].Text).GetGetMethod().Invoke(this.informations, null).ToString()
);
}
this.listViewInformations.Items.Add(new ListViewItem(val.ToArray()));
}
}

Comme vous le voyez, l'utilisation des attributs s'appuie beaucoup sur la réflexion.
Dans le détail cette fonction obtient la totalité des propriétés de la classe, et extrait pour chaque propriété la liste des attributs de type DisplayAttribute.
Ensuite si la propriété doit être affichée alors le nom de la propriété est ajouté comme colonne dans le ListView.
La suite est assez simple, il s'agit de remplir la ligne avec les informations.

V - Conclusion

L'utilisation des attributs personnalisés est très utile afin de paramétrer une classe au moment du développement pour influencer le comportement de composant utilisant cette classe.
C'est un moyen simple et efficace de fournir des informations sur la façon d'utiliser une classe.

VI - Téléchargement

Les sources de l'exemple sur les attributs personnalisés.