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.
Le 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.
Un 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 codice_1 de la manière suivante :
 [STAThread]
 public static void Main()
 ...
L'attribut associé à la méthode codice_1 est nommé codice_3 et ne comporte aucun paramètre.
Nom raccourci ou nom long.
Dans l'exemple précédent, l'attribut codice_3 est en fait géré par une classe nommée codice_5 qui dérive de la classe codice_6.
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 codice_7. 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.
Deux 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 :
Plusieurs attributs par déclaration.
Plusieurs 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.
Un 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 ( codice_8 ).
Exemple : 
[assembly: AssemblyTitle("Essai")]
Les cibles possibles sont :
Créer un nouvel attribut.
Le 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.
Pour créer un nouvel attribut, il faut créer une classe dérivant de la classe codice_6, et la nommer avec le suffixe codice_7. 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.
Les 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)
[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.
Les 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.
Par défaut l'attribut concerne tous les types de cibles (codice_29).
Pour définir les cibles que l'attribut peut concerner, il faut utiliser l'attribut codice_30 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 codice_31 possède les valeurs suivantes :
Accès dynamique aux attributs.
L'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 codice_48 représente un type de données : une classe, une structure.
L'opérateur codice_49 retourne le codice_48 de son argument.
Exemple :
Type maclasse = typeof(UneClasse);
La classe codice_6 possède une méthode statique nommée codice_52 prenant un codice_48 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 codice_54 permet de tester le type réel de l'attribut, et par exemple retrouver l'attribut codice_55 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.
Le langage C# définit un nombre d'attributs ayant un rôle spécifique lors de la compilation.
Attribut codice_56.
L'attribut codice_57 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 codice_58 est souvent utilisé pour distinguer les versions "Debug" et "Release" des projets sous Visual Studio.
Exemple :
[Conditional("DEBUG")]
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")]
Cet attribut évite d'encadrer systématiquement chaque appel de la méthode par des directives codice_59.
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 codice_60.
L'attribut codice_61 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 codice_62.
L'attribut codice_63 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 codice_64 indiquant les cibles acceptables pour l'attribut.
Le paramètre codice_65 (codice_66, codice_67 par défaut) indique si l'attribut peut être utilisé plusieurs fois pour la même cible.
Le paramètre codice_68 (codice_66, codice_70 par défaut) indique si l'attribut est hérité par les sous-classes.
Attribut codice_71.
L'attribut codice_72 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 codice_73.
L'attribut codice_74 s'applique aux énumérations pour indiquer que plusieurs valeurs peuvent être combinées avec l'opérateur ou ( codice_75 ). Cet attribut indique au compilateur de gérer les combinaisons de constantes dans la méthode ToString() de cette énumération.
Attribut codice_76.
L'attribut codice_77 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.