Attributs et Réflexion
Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 44 sur 70.
Les attributs sont des balises de métadonnées que vous attachez à des éléments de code tels que des classes, des méthodes ou des propriétés. Ils ne modifient pas directement la manière dont le code s'exécute, mais ils fournissent des informations qui peuvent être lues au moment de l'exécution. La réflexion est le mécanisme qui lit ces métadonnées.
Vous appliquez un attribut en le plaçant entre crochets au-dessus de l'élément cible :
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Vous pouvez créer des attributs personnalisés en héritant de Attribute :
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }La réflexion vous permet d'inspecter les types et de lire les attributs au moment de l'exécution en utilisant l'espace de noms System.Reflection :
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnVous pouvez également découvrir des méthodes, des propriétés et les invoquer dynamiquement :
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);Les attributs et la réflexion propulsent de nombreux frameworks : les bibliothèques de sérialisation les utilisent pour contrôler la sortie JSON, les frameworks de test les utilisent pour découvrir les méthodes de test, et les conteneurs d'injection de dépendances les utilisent pour connecter les services automatiquement.
Défi
FacileConstruisons un système de découverte de plugins qui utilise des attributs personnalisés et la réflexion pour trouver et décrire automatiquement les classes marquées comme plugins. C'est ainsi que les frameworks réels découvrent des composants, des méthodes de test ou des points de terminaison d'API au moment de l'exécution.
Vous organiserez votre code sur trois fichiers :
PluginAttribute.cs: Créez un attribut personnalisé appeléPluginAttributedans l'espace de nomsPluginSystem. Votre attribut doit :- Hériter de
Attribute - Avoir deux propriétés en lecture seule :
Name(string) etVersion(string) - Accepter les deux valeurs via son constructeur
- Hériter de
Plugins.cs: Créez plusieurs classes de plugins dans l'espace de nomsPluginSystem. Certaines doivent être décorées avec votrePluginAttribute, et une ne doit pas l'être :ImageProcessor- marqué avec[Plugin("Image Processor", "1.0")]DataExporter- marqué avec[Plugin("Data Exporter", "2.5")]HelperUtility- NON marqué par aucun attribut (c'est juste une classe ordinaire)
Program.cs: Dans votre fichier principal, utilisez la réflexion pour découvrir toutes les classes qui possèdent lePluginAttribute. Pour chaque plugin découvert, affichez ses informations. Vous devrez :- Récupérer tous les types de l'assembly actuel
- Vérifier chaque type pour la présence du
PluginAttribute - Extraire et afficher les propriétés
NameetVersionde l'attribut
Utilisez System.Reflection pour inspecter les types et récupérer les attributs personnalisés. La méthode GetCustomAttribute<T>() renvoie null si l'attribut n'est pas présent, vous pouvez donc l'utiliser pour filtrer les classes qui sont de réels plugins.
Affichez chaque plugin découvert dans ce format, un par ligne :
Plugin: {Name} (v{Version})Les plugins doivent être affichés dans l'ordre : ImageProcessor en premier, puis DataExporter. La classe HelperUtility ne doit pas apparaître dans la sortie car elle n'a pas l'attribut.
Sortie attendue :
Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)Ce défi démontre comment les frameworks utilisent les attributs comme marqueurs et la réflexion pour les découvrir - la même technique alimente les conteneurs d'injection de dépendances, les bibliothèques de sérialisation et les frameworks de test !
Aide-mémoire
Les attributs sont des balises de métadonnées placées entre crochets au-dessus des éléments de code. Ils fournissent des informations qui peuvent être lues au moment de l'exécution :
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Créez des attributs personnalisés en héritant de Attribute :
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }La réflexion vous permet d'inspecter les types et de lire les attributs au moment de l'exécution en utilisant l'espace de noms System.Reflection :
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnDécouvrez les méthodes et les propriétés de manière dynamique :
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);La méthode GetCustomAttribute<T>() renvoie null si l'attribut n'est pas présent, ce qui vous permet de filtrer les types en fonction de la présence de l'attribut.
Essayez vous-même
using System;
using System.Reflection;
using PluginSystem;
class Program
{
public static void Main(string[] args)
{
// TODO: Obtenir tous les types de l'assembly actuel
// Astuce : Utilisez Assembly.GetExecutingAssembly().GetTypes()
// TODO: Parcourir chaque type et vérifier s'il possède l'attribut PluginAttribute
// Astuce : Utilisez type.GetCustomAttribute<PluginAttribute>()
// TODO: Si l'attribut existe, afficher les informations du plugin au format :
// Plugin: {Name} (v{Version})
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et Utilisateur