Partager via


Définir et lire des attributs personnalisés

Les attributs permettent d’associer des informations à du code de manière déclarative. Ils peuvent également fournir un élément réutilisable qui peut être appliqué à différentes cibles. Considérez le ObsoleteAttribute. Il peut être appliqué aux classes, aux structs, aux méthodes, aux constructeurs, etc. Il déclare que l’élément est obsolète. Il appartient ensuite au compilateur C# de rechercher cet attribut et d’effectuer une action en réponse.

Dans ce tutoriel, vous allez apprendre à ajouter des attributs à votre code, à créer et à utiliser vos propres attributs et à utiliser certains attributs intégrés à .NET.

Conditions préalables

Vous devez configurer votre ordinateur pour exécuter .NET. Vous trouverez les instructions d’installation sur la page Téléchargements .NET . Vous pouvez exécuter cette application sur Windows, Ubuntu Linux, macOS ou dans un conteneur Docker. Vous devez installer votre éditeur de code favori. Les descriptions suivantes utilisent Visual Studio Code, qui est un éditeur open source multiplateforme. Toutefois, vous pouvez utiliser les outils que vous êtes à l’aise avec.

Créer l’application

Maintenant que vous avez installé tous les outils, créez une application console .NET. Pour utiliser le générateur de ligne de commande, exécutez la commande suivante dans votre interpréteur de commandes favori :

dotnet new console

Cette commande crée des fichiers projet .NET nus. Vous exécutez dotnet restore pour restaurer les dépendances nécessaires pour compiler ce projet.

Vous n’avez pas besoin d’exécuter dotnet restore, car il est exécuté implicitement par toutes les commandes qui nécessitent une restauration pour se produire, comme dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish et dotnet pack. Pour désactiver la restauration implicite, utilisez l’option --no-restore .

La commande dotnet restore est toujours utile dans certains scénarios où la restauration explicite est logique, comme les builds d’intégration continue dans Azure DevOps Services ou dans les systèmes de génération qui doivent contrôler explicitement le moment où la restauration se produit.

Pour plus d’informations sur la gestion des flux NuGet, consultez la documentation dotnet restore.

Pour exécuter le programme, utilisez dotnet run. Vous devez voir la sortie « Hello, World » dans la console.

Ajouter des attributs au code

En C#, les attributs sont des classes qui héritent de la Attribute classe de base. Toute classe qui hérite de Attribute peut être utilisée comme une sorte de « balise » sur d’autres éléments de code. Par exemple, il existe un attribut appelé ObsoleteAttribute. Cet attribut signale que le code est obsolète et ne doit plus être utilisé. Vous placez cet attribut sur une classe, par exemple, à l’aide de crochets.

[Obsolete]
public class MyClass
{
}

Bien que la classe soit appelée ObsoleteAttribute, il est nécessaire d’utiliser [Obsolete] uniquement dans le code. La plupart du code C# suit cette convention. Vous pouvez utiliser le nom [ObsoleteAttribute] complet si vous le souhaitez.

Lorsque vous marquez une classe obsolète, il est judicieux de fournir des informations sur la raison pour laquelle elle est obsolète et/ou sur ce qu’il faut utiliser à la place. Vous incluez un paramètre de chaîne à l’attribut Obsolète pour fournir cette explication.

[Obsolete("ThisClass is obsolete. Use ThisClass2 instead.")]
public class ThisClass
{
}

La chaîne est passée en tant qu’argument à un ObsoleteAttribute constructeur, comme si vous écriviez var attr = new ObsoleteAttribute("some string").

Les paramètres d’un constructeur d’attribut sont limités aux types/littéraux simples : bool, int, double, string, Type, enums, etc et aux tableaux de ces types. Vous ne pouvez pas utiliser une expression ou une variable. Vous êtes libre d’utiliser des paramètres positionnels ou nommés.

Créer votre propre attribut

Vous créez un attribut en définissant une nouvelle classe qui hérite de la Attribute classe de base.

public class MySpecialAttribute : Attribute
{
}

Avec le code précédent, vous pouvez utiliser [MySpecial] (ou [MySpecialAttribute]) comme attribut ailleurs dans la base de code.

[MySpecial]
public class SomeOtherClass
{
}

Attributs dans la bibliothèque de classes de base .NET, comme ObsoleteAttribute déclencher certains comportements au sein du compilateur. Toutefois, tout attribut que vous créez agit uniquement en tant que métadonnées et n’entraîne pas d’exécution de code dans la classe d’attributs. C’est à vous d’agir sur ces métadonnées ailleurs dans votre code.

Il y a un 'gotcha' ici pour regarder. Comme mentionné précédemment, seuls certains types peuvent être passés en tant qu’arguments lors de l’utilisation d’attributs. Toutefois, lors de la création d’un type d’attribut, le compilateur C# ne vous empêche pas de créer ces paramètres. Dans l’exemple suivant, vous avez créé un attribut avec un constructeur qui compile correctement.

public class GotchaAttribute : Attribute
{
    public GotchaAttribute(Foo myClass, string str)
    {
    }
}

Toutefois, vous ne pouvez pas utiliser ce constructeur avec la syntaxe d’attribut.

[Gotcha(new Foo(), "test")] // does not compile
public class AttributeFail
{
}

Le code précédent provoque une erreur de compilateur telle que Attribute constructor parameter 'myClass' has type 'Foo', which is not a valid attribute parameter type

Comment restreindre l’utilisation des attributs

Les attributs peuvent être utilisés sur les « cibles » suivants. Les exemples précédents les montrent sur les classes, mais ils peuvent également être utilisés sur :

  • Assemblée
  • classe
  • Constructeur
  • Déléguer
  • Énumération
  • Événement
  • Terrain
  • GenericParameter
  • Interface
  • Méthode
  • Module
  • Paramètre
  • Propriété
  • ReturnValue
  • Struct

Lorsque vous créez une classe d’attributs, par défaut, C# vous permet d’utiliser cet attribut sur l’une des cibles d’attribut possibles. Si vous souhaitez limiter votre attribut à certaines cibles, vous pouvez le faire à l’aide de la AttributeUsageAttribute classe d’attributs. C’est juste, un attribut sur un attribut !

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class MyAttributeForClassAndStructOnly : Attribute
{
}

Si vous tentez de placer l’attribut ci-dessus sur un élément qui n’est pas une classe ou un struct, vous obtenez une erreur du compilateur comme Attribute 'MyAttributeForClassAndStructOnly' is not valid on this declaration type. It is only valid on 'class, struct' declarations

public class Foo
{
    // if the below attribute was uncommented, it would cause a compiler error
    // [MyAttributeForClassAndStructOnly]
    public Foo()
    { }
}

Comment utiliser des attributs attachés à un élément de code

Les attributs agissent comme des métadonnées. Sans force extérieure, ils ne font rien.

Pour rechercher et agir sur les attributs, la réflexion est nécessaire. La réflexion vous permet d’écrire du code en C# qui examine d’autres codes. Par exemple, vous pouvez utiliser Reflection pour obtenir des informations sur une classe(ajouter using System.Reflection; à la tête de votre code) :

TypeInfo typeInfo = typeof(MyClass).GetTypeInfo();
Console.WriteLine("The assembly qualified name of MyClass is " + typeInfo.AssemblyQualifiedName);

Cela imprime quelque chose comme : The assembly qualified name of MyClass is ConsoleApplication.MyClass, attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

Une fois que vous avez un TypeInfo objet (ou un MemberInfo, FieldInfoou un autre objet), vous pouvez utiliser la GetCustomAttributes méthode. Cette méthode retourne une collection d’objets Attribute . Vous pouvez également utiliser GetCustomAttribute et spécifier un type d’attribut.

Voici un exemple d’utilisation GetCustomAttributes sur une MemberInfo instance pour MyClass laquelle nous avons vu précédemment un [Obsolete] attribut dessus.

var attrs = typeInfo.GetCustomAttributes();
foreach(var attr in attrs)
    Console.WriteLine("Attribute on MyClass: " + attr.GetType().Name);

Cette impression s’affiche dans la console : Attribute on MyClass: ObsoleteAttribute. Essayez d’ajouter d’autres attributs à MyClass.

Il est important de noter que ces Attribute objets sont instanciés de manière instanciée. Autrement dit, ils ne sont pas instanciés tant que vous n’utilisez GetCustomAttribute pas ou GetCustomAttributes. Ils sont également instanciés chaque fois. L’appel GetCustomAttributes deux fois dans une ligne retourne deux instances différentes de ObsoleteAttribute.

Attributs courants dans le runtime

Les attributs sont utilisés par de nombreux outils et infrastructures. NUnit utilise des attributs tels [Test] que et [TestFixture] utilisés par l’exécuteur de test NUnit. ASP.NET MVC utilise des attributs comme [Authorize] et fournit un framework de filtre d’actions pour effectuer des préoccupations croisées sur les actions MVC. PostSharp utilise la syntaxe d’attribut pour autoriser la programmation orientée aspect en C#.

Voici quelques attributs notables intégrés aux bibliothèques de classes de base .NET Core :

  • [Obsolete]. Celui-ci a été utilisé dans les exemples ci-dessus, et il réside dans l’espace System de noms. Il est utile de fournir une documentation déclarative sur une base de code changeant. Un message peut être fourni sous la forme d’une chaîne, et un autre paramètre booléen peut être utilisé pour passer d’un avertissement du compilateur à une erreur du compilateur.
  • [Conditional]. Cet attribut se trouve dans l’espace System.Diagnostics de noms. Cet attribut peut être appliqué aux méthodes (ou aux classes d’attributs). Vous devez passer une chaîne au constructeur. Si cette chaîne ne correspond pas à une #define directive, le compilateur C# supprime les appels à cette méthode (mais pas la méthode elle-même). En règle générale, vous utilisez cette technique à des fins de débogage (diagnostics).
  • [CallerMemberName]. Cet attribut peut être utilisé sur les paramètres et réside dans l’espace System.Runtime.CompilerServices de noms. CallerMemberName est un attribut utilisé pour injecter le nom de la méthode qui appelle une autre méthode. Il s’agit d’un moyen d’éliminer les « chaînes magiques » lors de l’implémentation d’INotifyPropertyChanged dans différents frameworks d’interface utilisateur. Par exemple :
public class MyUIClass : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public void RaisePropertyChanged([CallerMemberName] string propertyName = default!)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string? _name;
    public string? Name
    {
        get { return _name;}
        set
        {
            if (value != _name)
            {
                _name = value;
                RaisePropertyChanged();   // notice that "Name" is not needed here explicitly
            }
        }
    }
}

Dans le code ci-dessus, vous n’avez pas besoin d’avoir de chaîne littérale "Name" . L’utilisation CallerMemberName empêche les bogues liés aux fautes de frappe et facilite également la refactorisation/le renommage plus fluide. Les attributs apportent une puissance déclarative à C#, mais ils sont une forme de méta-données de code et n’agissent pas par eux-mêmes.