Introduction à la Réflexion
La réflexion en C# est un mécanisme permettant d'inspecter et de manipuler dynamiquement les métadonnées des assemblages. Les fichiers DLL et EXE contiennent des métadonnées qui décrivent les types, propriétés, méthodes et champs définis dans l'assemblage. Contrairement aux EXE, les DLL n'ont pas de point d'entrée et sont conçues pour être réutilisées dans d'autres applications. La réflexion offre des capacités telles que le chargement dynamique d'assemblages, l'instanciation de types, et l'invocation de méthodes, ce qui est essentiel dans des frameworks comme ASP.NET MVC, les ORM, et les conteneurs IoC.
Chargement Dynamique d'Assemblages
Pour utiliser la réflexion, il faut d'abord charger un assemblage (DLL). Voici plusieurs méthodes :
// Méthode 1 : Chargement par nom d'assemblage (répertoire courant)
Assembly assemblage = Assembly.Load("MaLibrairie");
// Méthode 2 : Chargement par chemin complet
Assembly assemblage = Assembly.LoadFile(@"C:\Chemin\vers\MaLibrairie.dll");
// Méthode 3 : Chargement par nom complet avec chemin
Assembly assemblage = Assembly.LoadFrom(@"C:\Chemin\vers\MaLibrairie.dll");
// Parcourir les types et méthodes de l'assemblage chargé
foreach (Type type in assemblage.GetTypes())
{
Console.WriteLine($"Type : {type.Name}");
foreach (MethodInfo methode in type.GetMethods())
{
Console.WriteLine($" Méthode : {methode.Name}");
}
}
Le choix de la méthode dépend de l'emplacement du fichier. Si l'assemblage est référencé dans le projet, Load peut être utilisé ; sinon, spécifiez le chemin complet.
Création d'Objets via la Réflexion
Avec un consrtucteur sans paramètre
// Charger l'assemblage et obtenir le type complet
Assembly assemblage = Assembly.LoadFrom("MaLibrairie.dll");
Type typeCible = assemblage.GetType("EspaceNoms.MaClasse");
// Créer une instance de l'object
object instance = Activator.CreateInstance(typeCible);
// Convertir en interface si nécessaire
IMonInterface monObjet = instance as IMonInterface;
monObjet?.Exécuter();
Avec des constructeurs paramétrés
Type typeParam = assemblage.GetType("EspaceNoms.ClasseAvecParamètres");
// Créer avec différents paramètres
object instance1 = Activator.CreateInstance(typeParam, new object[] { "valeur" });
object instance2 = Activator.CreateInstance(typeParam, new object[] { 123, "texte" });
Constructeur privé
La réflexion peut instancier des classes avec des constructeurs privés, ce qui permet de contourner la visibilité.
Type typePrivé = assemblage.GetType("EspaceNoms.ClassePrivée");
object instancePrivée = Activator.CreateInstance(typePrivé, true); // Le second paramètre permet d'accéder aux constructeurs privés
Création de Classes Génériques
Pour les classes génériques, uitlisez le nom avec le nombre de paramètres typographiques, suivi de MakeGenericType.
Type typeGénérique = assemblage.GetType("EspaceNoms.ClasseGenerique`3");
Type typeSpécifié = typeGénérique.MakeGenericType(new[] { typeof(int), typeof(string), typeof(DateTime) });
object instanceGénérique = Activator.CreateInstance(typeSpécifié);
Invocation de Méthodes
Méthodes publiques et surchargées
Type typeMéthodes = assemblage.GetType("EspaceNoms.ClasseAMéthodes");
object instanceMéthodes = Activator.CreateInstance(typeMéthodes);
// Appel d'une méthode sans paramètre
MethodInfo méthodeSimple = typeMéthodes.GetMethod("MéthodeSansParam");
méthodeSimple.Invoke(instanceMéthodes, null);
// Appel d'une méthode surchargée avec paramètres spécifiques
MethodInfo méthodeSurchargée = typeMéthodes.GetMethod("MéthodeSurchargée", new[] { typeof(int), typeof(string) });
méthodeSurchargée.Invoke(instanceMéthodes, new object[] { 42, "exemple" });
// Appel d'une méthode statique
MethodInfo méthodeStatique = typeMéthodes.GetMethod("MéthodeStatique");
méthodeStatique.Invoke(null, new object[] { "données" }); // null pour l'instance
Méthodes privées
MethodInfo méthodePrivée = typeMéthodes.GetMethod("MéthodePrivée", BindingFlags.Instance | BindingFlags.NonPublic);
méthodePrivée.Invoke(instanceMéthodes, new object[] { "paramètre" });
Méthodes génériques
Type typeAvecGénérique = assemblage.GetType("EspaceNoms.ClasseAvecMéthodeGénérique");
object instanceGénérique = Activator.CreateInstance(typeAvecGénérique);
MethodInfo méthodeGénériqueBase = typeAvecGénérique.GetMethod("MéthodeGénérique");
MethodInfo méthodeGénériqueSpécifiée = méthodeGénériqueBase.MakeGenericMethod(new[] { typeof(double), typeof(bool) });
méthodeGénériqueSpécifiée.Invoke(instanceGénérique, new object[] { 3.14, true });
Manipulation des Champs et Propriétés
La réflexion permet d'accéder aux champs et propriétés d'un objet pour les lire ou les modifier.
Type typePropriétés = assemblage.GetType("EspaceNoms.Étudiant");
object instancePropriétés = Activator.CreateInstance(typePropriétés);
// Parcourir et modifier les propriétés
foreach (PropertyInfo prop in typePropriétés.GetProperties())
{
Console.WriteLine($"Propriété : {prop.Name}, Valeur : {prop.GetValue(instancePropriétés)}");
if (prop.Name == "Identifiant")
prop.SetValue(instancePropriétés, 100);
}
// Accéder aux champs publics
FieldInfo champ = typePropriétés.GetField("ChampPublic");
champ.SetValue(instancePropriétés, "nouvelle valeur");