Programmation C sharp/Attributs
En C#, chaque déclaration de classe, méthode, propriété, ou variable peut être précédée d'un ou plusieurs attributs.
Rôle d'un attribut
modifierLe rôle d'un attribut est d'associer des informations, des méta-données à une déclaration. Ces données peuvent être utilisées par le compilateur, ou le Framework .NET, ou par l'application elle-même.
Syntaxe d'utilisation
modifierUn attribut comporte un nom et éventuellement des paramètres.
Il est encadré par des crochets. Par exemple, Visual Studio .NET génère la fonction Main()
de la manière suivante :
...
[STAThread]
public static void Main()
{
...
}
...
L'attribut associé à la méthode Main()
est nommé STAThread
et ne comporte aucun paramètre.
Nom raccourci ou nom long
modifierDans l'exemple précédent, l'attribut STAThread
est en fait géré par une classe nommée STAThreadAttribute
qui dérive de la classe System.Attribute
.
L'exemple fonctionne également avec le code source suivant :
[STAThreadAttribute]
public static void Main()
{
...
}
Par convention, toute classe définissant un attribut doit porter un nom se terminant par Attribute
. Ce suffixe est optionnel, le compilateur recherchera d'abord le nom indiqué, puis s'il ne trouve pas la classe, il ajoute le suffixe pour effectuer une seconde recherche.
Attribut avec paramètres
modifierDeux exemples d'attribut comportant des paramètres :
Exemple 1:
[Obsolete("Veuillez ne plus utiliser cette méthode.")]
public void AncienneMethode()
{
...
}
Exemple 2:
[DllImport("User32.dll", EntryPoint="MessageBox")]
static extern int MessageDialog(int hWnd, string msg, string caption, int msgType);
Un attribut possède deux catégories de paramètres :
- Les paramètres ordonnés : ils ne sont pas nommés, et doivent être placés dans le bon ordre, et avant les paramètres nommés ;
- Les paramètres nommés : le nom du paramètre est suivi du signe égal et de la valeur affectée au paramètre.
Plusieurs attributs par déclaration
modifierPlusieurs attributs peuvent être associés à une déclaration. Il est possible de les mettre dans le même bloc de crochets, en les séparant par une virgule :
[Obsolete("Veuillez ne plus utiliser cette méthode."),
DllImport("User32.dll", EntryPoint="MessageBox")]
static extern int MessageDialog(int hWnd, string msg, string caption, int msgType);
Ou bien de les mettre dans des blocs différents :
[Obsolete("Veuillez ne plus utiliser cette méthode.")]
[DllImport("User32.dll", EntryPoint="MessageBox")]
static extern int MessageDialog(int hWnd, string msg, string caption, int msgType);
Cible d'un attribut
modifierUn attribut est associé à la déclaration qui suit (sa cible), mais il existe des cas où la cible est ambiguë. En effet, il existe des attributs globaux associés à l'assembly lui-même et ne concerne pas une déclaration particulière. De même, les attributs concernant la valeur de retour d'une méthode sont placés au même endroit que ceux concernant la méthode elle-même.
Pour lever l'ambiguïté, il est possible de préciser la cible des attributs contenus dans le bloc de crochets. Le nom de la cible est suivi du signe deux-points ( :
).
Exemple :
[assembly: AssemblyTitle("Essai")]
Les cibles possibles sont :
assembly
: attributs concernant l'assembly (ne précède aucune déclaration particulière),module
: attributs concernant le module (ne précède aucune déclaration particulière),type
: attributs concernant la classe, la structure, l'interface, l'énumération ou le delegué,method
: attributs concernant la méthode, l'accesseurget
ouset
d'une propriété, l'accesseuradd
ouremove
d'un évènement, ou l'évènement,return
: attributs concernant la valeur retournée par la méthode, le délégue, l'accesseurget
ouset
d'une propriété,param
: attributs concernant un paramètre (méthode, délégue, accesseurset
d'une propriété, accesseuradd
ouremove
d'un évènement),field
: attributs concernant le champ, ou l'évènement,property
: attributs concernant la propriété ou l'indexeur,event
: attributs concernant l'évènement.
Créer un nouvel attribut
modifierLe langage C# permet de créer de nouveaux attributs pour, par exemple, documenter une partie du code ou associer des données particulières à une déclaration.
Créer la classe
modifierPour créer un nouvel attribut, il faut créer une classe dérivant de la classe System.Attribute
, et la nommer avec le suffixe Attribute
. Exemple :
using System;
public class ExempleAttribute : Attribute
{
}
Cet attribut peut déjà être utilisé tel qu'il est, sans paramètres :
[Exemple]
public class UneClasse
{
[method:Exemple]
[return:Exemple]
public int UneMethode(int UnParametre)
{
return UnParametre;
}
}
Définir les paramètres positionnels
modifierLes paramètres positionnels (ou ordonnés) correspondent aux paramètres du constructeur. Exemple :
using System;
public class ExempleAttribute : Attribute
{
private string titre;
public ExempleAttribute(string titre)
{ this.titre = titre; }
}
[Exemple("Un exemple de classe")]
public class UneClasse
{
[method:Exemple("Une méthode")]
[return:Exemple("Retourne le paramètre passé")]
public int UneMethode(int UnParametre)
{
return UnParametre;
}
}
Un attribut peut avoir plusieurs constructeurs différents pour définir différents types de paramètres positionnels. Exemple :
using System;
public class ExempleAttribute : Attribute
{
private string titre,commentaire;
public ExempleAttribute(string titre)
{
this.titre = titre;
this.commentaire = "Sans commentaire";
}
public ExempleAttribute(string titre,string commentaire)
{
this.titre = titre;
this.commentaire = commentaire;
}
}
[Exemple("Un exemple de classe",
"Cette classe est un exemple")]
public class UneClasse
{
[method:Exemple("Une méthode")]
[return:Exemple("Retourne le paramètre passé")]
public int UneMethode(int UnParametre)
{
return UnParametre;
}
}
Définir les paramètres nommés
modifierLes paramètres nommés correspondent à des champs ou propriétés publics, ils sont optionnels :
using System;
public class ExempleAttribute : Attribute
{
private string titre;
public ExempleAttribute(string titre)
{
this.titre = titre;
numero = 0; // valeur par défaut
}
public int numero;
}
[Exemple("Un exemple de classe", numero=1)]
public class UneClasse
{
[method:Exemple("Une méthode")]
[return:Exemple("Retourne le paramètre passé", numero=2)]
public int UneMethode(int UnParametre)
{
return UnParametre;
}
}
Définir la cible
modifierPar défaut l'attribut concerne tous les types de cibles (All
).
Pour définir les cibles que l'attribut peut concerner, il faut utiliser l'attribut System.AttributeUsage
sur la classe de l'attribut. Exemple :
using System;
[ AttributeUsage( AttributeTargets.Class | // cible classe
AttributeTargets.Struct, // ou structure
AllowMultiple = false ) ] // une seule fois par classe ou structure
public class ExempleAttribute : Attribute
{
private string titre;
public ExempleAttribute(string titre)
{
this.titre = titre;
numero = 0; // valeur par défaut
}
public int numero;
}
[Exemple("Un exemple de classe", numero=1)]
public class UneClasse
{
// erreur de compilation pour les 2 attributs suivants
// car l'attribut ne peut concerner une méthode ou une valeur de retour
[method:Exemple("Une méthode")]
[return:Exemple("Retourne le paramètre passé", numero=2)]
public int UneMethode(int UnParametre)
{
return UnParametre;
}
}
// erreur de compilation car l'attribut est utilisé plus d'une fois
[Exemple("Un autre exemple de classe", numero=1)]
[Exemple("Un exemple de classe", numero=2)]
public class UneAutreClasse
{
}
L'énumération AttributeTargets
possède les valeurs suivantes :
All
: toutes les cibles possibles. Cette valeur est la combinaison par ou logique de toutes les autres valeurs,Assembly
,Module
,Class
: déclaration d'une classe,Struct
: déclaration d'une structure,Interface
: déclaration d'une interface,Constructor
: constructeur d'une classe,Delegate
,Event
,Enum
,Field
,Method
: déclaration d'une méthode,Property
: déclaration d'une propriété,Parameter
,ReturnValue
,GenericParameter
: paramètre d'un générique (template).
Accès dynamique aux attributs
modifierL'accès aux attributs personnalisés se fait en utilisant la réflexion qui permet d'accéder dynamiquement aux différents éléments des déclarations (attributs, méthodes d'une classe, ...).
La classe Type
représente un type de données : une classe, une structure.
L'opérateur typeof
retourne le Type
de son argument.
Exemple :
Type maclasse = typeof(UneClasse);
La classe System.Attribute
possède une méthode statique nommée GetCustomAttributes
prenant un Type
en paramètre (ou tout autre objet de réflexion tel que méthode, propriété, ...) et retourne un tableau d'attributs :
Type maclasse = typeof(UneClasse);
System.Attribute[] attributs = System.Attribute.GetCustomAttributes(maclasse);
L'opérateur is
permet de tester le type réel de l'attribut, et par exemple retrouver l'attribut Exemple
définit dans la section précédente :
foreach(Attribute attr in attributs)
if (attr is ExempleAttribute)
{
ExempleAttribute ex=(ExempleAttribute)attr;
Console.WriteLine("Exemple : " + ex.titre );
}
Attributs prédéfinis
modifierLe langage C# définit un nombre d'attributs ayant un rôle spécifique lors de la compilation.
Attribut Conditional
modifier
L'attribut System.Diagnostics.ConditionalAttribute
s'applique à une méthode qui ne doit être appelée et définie que si le symbole spécifié est défini.
Il peut s'agir par exemple d'une méthode de déboggage.
Le symbole DEBUG
est souvent utilisé pour distinguer les versions Debug et Release des projets sous Visual Studio.
Exemple :
[Conditional("DEBUG")]
public void trace(string message) { ... }
Si l'attribut est utilisé plus d'une fois, la méthode n'est appelée et définie que si l'un des symboles est défini (Ou logique) :
[Conditional("DEBUG"),Conditional("FORCEDEBUG")]
public void trace(string message) { ... }
Cet attribut évite d'encadrer systématiquement chaque appel de la méthode par des directives #if...#endif
.
Il est également applicable aux classes d'attributs. Dans ce cas, les informations associées à l'attribut ne sont ajoutées que si le symbole est défini.
Par exemple :
[Conditional("DEBUG")]
public class Documentation : System.Attribute
{
string text;
public Documentation(string text)
{
this.text = text;
}
}
class ExempleDeClasse
{
// Cet attribut ne sera inclus que si DEBUG est défini.
[Documentation("Cette méthode affiche un entier.")]
static void DoWork(int i)
{
System.Console.WriteLine(i.ToString());
}
}
Attribut Obsolete
modifier
L'attribut System.ObsoleteAttribute
est utilisé pour marquer une entité dont l'utilisation n'est plus recommandée. Le compilateur peut alors générer une erreur ou un avertissement selon les paramètres de l'attribut.
[Obsolete("Utilisez plutôt UneNouvelleMethode()")]
public void UneAncienneMethode()
{ ... }
Lors de l'appel à la méthode :
UneAncienneMethode();
le compilateur génèrera un avertissement comportant le message spécifié :
Utilisez plutôt UneNouvelleMethode()
Si une valeur booléenne est spécifée à la suite du message, elle indique si une erreur doit être générée au lieu d'un avertissement.
Exemple :
[Obsolete("Utilisez plutôt UneNouvelleMethode()",true)]
public void UneAncienneMethode()
{ ... }
Lors de l'appel à cette méthode, le compilateur génèrera une erreur comportant le message spécifié :
Utilisez plutôt UneNouvelleMethode()
Attribut AttributeUsage
modifier
L'attribut System.AttributeUsageAttribute
déjà vu précédemment, indique l'utilisation d'une classe attribut.
Le seul paramètre obligatoire est une valeur ou une combinaison de valeurs de l'énumération System.AttributeTargets
indiquant les cibles acceptables pour l'attribut.
Le paramètre AllowMultiple
(bool
, false
par défaut) indique si l'attribut peut être utilisé plusieurs fois pour la même cible.
Le paramètre Inherited
(bool
, true
par défaut) indique si l'attribut est hérité par les sous-classes.
Attribut DllImport
modifier
L'attribut System.Runtime.InteropServices.DllImportAttribute
permet d'importer une fonction définie dans une DLL externe.
Le nom de la DLL où la fonction est définie est le seul paramètre obligatoire.
Attribut Flags
modifier
L'attribut System.FlagsAttribute
s'applique aux énumérations pour indiquer que plusieurs valeurs peuvent être combinées avec l'opérateur ou ( |
). Cet attribut indique au compilateur de gérer les combinaisons de constantes dans la méthode ToString() de cette énumération.
Attribut ThreadStatic
modifier
L'attribut System.ThreadStaticAttribute
s'applique aux variables membres statiques. Cet attribut indique que la variable est allouée pour tout nouveau thread. Ce qui signifie que chaque thread possède une instance différente de la variable. L'initialisation d'une telle variable à la déclaration n'est effectuée que pour l'instance du thread ayant chargé la classe.