Introduction : la magie du reflet
Imaginez que vous écrivez un code qui peut "se regarder" lui-même. Pas comme dans un miroir déformant, mais comme dans un appareil à rayons X — voir la structure, les propriétés, les méthodes, et même les exécuter, sans connaître le nom de la classe au moment de la compilation. Ce n'est pas de la magie, c'est la réflexion en C# et sa "petite sœur" — l'introspection C#.
Pourquoi en avoir besoin ? Imaginez que vous écrivez un système de plugins, un déserialiseur JSON, ou un appel dynamique de méthodes. Sans réflexion, vous devriez écrire une multitude de if-else. Avec elle, quelques lignes suffisent. Cela semble intrigant ? Allons voir cela.
Dans cet article, nous ne nous contenterons pas de survoler la théorie. Nous écrirons des exemples de code réels que vous pourrez utiliser immédiatement dans vos projets. Vous apprendrez à obtenir des informations sur n'importe quel type, à créer un objet "à partir de rien", à appeler une méthode privée et même à lire les attributs. Et tout cela, en langage C#, que l'on aime tant pour sa rigueur et sa puissance.
Qu'est-ce que la réflexion et l'introspection en C# ? Différence des concepts
Souvent, ces termes sont utilisés comme synonymes, mais il existe une nuance subtile entre eux :
- Introspection C# — c'est la capacité d'un programme à observer sa propre structure. Vous pouvez savoir quels champs une classe possède, quelles méthodes elle contient, quels attributs lui sont attachés. Mais vous ne modifiez rien.
- Réflexion C# — c'est un concept plus large. Elle inclut l'introspection, plus la possibilité d'interagir avec le code : appeler des méthodes, créer des objets, modifier les valeurs des champs.
En termes simples : l'introspection, c'est "regarder", la réflexion, c'est "regarder et toucher". En C#, les deux fonctions sont assurées par l'espace de noms System.Reflection. C'est votre outil principal.
Exemple pratique : obtenir des informations sur un type (Type)
Tout commence avec la classe Type. C'est le fondement de la réflexion. On peut l'obtenir de trois manières :
// Méthode 1 : via typeof()Type typeFromKeyword = typeof(string);
// Méthode 2 : via l'instance .GetType()string example = "Bonjour le monde !";Type typeFromInstance = example.GetType();
// Méthode 3 : via l'assembly (Assembly)Type typeFromAssembly = Assembly.GetExecutingAssembly().GetType("System.String");Maintenant, avec un objet Type, nous pouvons tout savoir sur la classe. Écrivons une méthode utile pour l'introspection :
using System;using System.Reflection;
public class Inspector{ public static void ShowTypeInfo(Type type) { Console.WriteLine($"=== Informations sur le type : {type.Name} ==="); Console.WriteLine($"Nom complet : {type.FullName}"); Console.WriteLine($"Est-ce une classe ? {type.IsClass}"); Console.WriteLine($"Est-ce abstrait ? {type.IsAbstract}"); Console.WriteLine($"Classe de base : {type.BaseType?.Name}"); Console.WriteLine("Propriétés publiques :"); foreach (PropertyInfo prop in type.GetProperties()) { Console.WriteLine($" - {prop.PropertyType.Name} {prop.Name}"); } Console.WriteLine("Méthodes publiques :"); foreach (MethodInfo method in type.GetMethods()) { Console.WriteLine($" - {method.ReturnType.Name} {method.Name}({string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType.Name} {p.Name}"))})"); } }}
// UtilisationInspector.ShowTypeInfo(typeof(List<int>));/* Sortie :=== Informations sur le type : List`1 ===Nom complet : System.Collections.Generic.List`1[[System.Int32, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]Est-ce une classe ? TrueEst-ce abstrait ? FalseClasse de base : Object
Propriétés publiques : - Int32 Capacity - Int32 Count
Méthodes publiques : - Void Add(Int32 obj) - Void Clear() - Boolean Contains(Int32 item) ... et bien d'autres*/C'est ce qu'on appelle de l'intro pure