1. Introduction
En C# n'importe quelle classe (ou struct) — c'est un ensemble de membres. Parmi eux :
- Champs (fields) : variables qui stockent des données.
- Propriétés (properties) : des wrappers “intelligents” avec get/set.
- Méthodes (methods) : la fonctionnalité.
- Constructeurs (constructors) : façons de créer une instance.
- Événements (events) : publish-subscribe de manière intelligente.
Imaginez que vous avez une loupe magique qui vous permet de regarder l'intérieur de n'importe quel type, de connaître son contenu et même de le modifier. Voilà ce qu'est la réflexion.
Espace de noms et types principaux de la réflexion
Avant de commencer, n'oubliez pas d'importer l'espace de noms :
using System.Reflection;
Classes et interfaces clés :
| Classe/interface | Description |
|---|---|
|
Entrée principale — tout commence par lui |
|
Information sur un champ |
|
Information sur une propriété |
|
Information sur une méthode |
|
Information sur un constructeur |
|
Information sur un événement |
|
“Parent” commun pour tous les précédents |
2. Obtenir les membres d'un type via la réflexion
Obtenir les champs et les propriétés
Exemple : énumérer fields et properties
Supposons qu'on ait cette classe :
public class Person
{
public string Name { get; set; }
public int Age;
private string Secret = "Secret word";
}
Obtenir les champs :
Type personType = typeof(Person);
FieldInfo[] fields = personType.GetFields(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
Console.WriteLine($"Champ: {field.Name}, Type: {field.FieldType.Name}, Accès: {field.Attributes}");
}
Obtenir les propriétés :
PropertyInfo[] properties = personType.GetProperties(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
Console.WriteLine($"Propriété: {property.Name}, Type: {property.PropertyType.Name}");
}
Explications
- BindingFlags — c'est un ensemble de “flags” qui indiquent quels membres chercher (public/private, instance/static, etc.).
- Champs et propriétés sont différents : un field est un morceau de données, une propriété est des méthodes get/set qui cachent un field.
Obtenir les méthodes
MethodInfo[] methods = personType.GetMethods(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var method in methods)
{
Console.WriteLine($"Méthode: {method.Name}");
}
Vous serez surpris du nombre de méthodes listées. Il y aura aussi les getters/setters des propriétés, et les méthodes utilitaires venant de object !
Filtre utile : pour obtenir seulement vos méthodes “explicites”, vous pouvez ajouter un filtrage par nom ou par attribut.
Quels BindingFlags utiliser pour quoi
| Ce qu'on cherche | Exemple d'appel | Description |
|---|---|---|
| Champs publics | |
Tout ce qui est visible depuis l'extérieur |
| Champs privés | |
Cachés, internes |
| Seulement instance | |
Seulement non-statiques |
| Seulement static | |
Seulement les membres statiques |
| Tout à la fois | |
Liste complète |
3. Création dynamique d'une instance de type
Pourquoi c'est cool ?
On peut créer un objet sans connaître son type exact à la compilation.
Méthode simple — via Activator.CreateInstance
// On obtient Type d'une manière ou d'une autre, par ex :
Type myType = typeof(Person);
// On crée l'objet :
object obj = Activator.CreateInstance(myType);
// Maintenant obj — c'est une instance de Person, mais comme object
Si la classe n'a pas de constructeur sans paramètres, on peut passer des paramètres :
public class Animal
{
public string Name { get; }
public Animal(string name) { Name = name; }
}
// ...
Type animalType = typeof(Animal);
object dog = Activator.CreateInstance(animalType, "Buddy");
Important !
Console.WriteLine(dog.GetType().Name); // Animal
Méthode plus personnalisée — via ConstructorInfo
ConstructorInfo ctor = animalType.GetConstructor(new Type[] { typeof(string) });
object dog = ctor.Invoke(new object[] { "Buddy" });
En pratique on utilise souvent Activator, mais parfois on a besoin de paramètres précis ou de travailler avec des constructeurs privés — là ConstructorInfo aide.
4. Accès aux champs et propriétés par nom
Obtenir et modifier un champ
Person p = new Person();
Type t = p.GetType();
FieldInfo field = t.GetField("Age");
field.SetValue(p, 42);
Console.WriteLine(field.GetValue(p)); // 42
Travailler avec des champs privés :
FieldInfo secret = t.GetField("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
if (secret != null)
{
secret.SetValue(p, "Oh, the secret is revealed");
Console.WriteLine(secret.GetValue(p));
}
else
{
Console.WriteLine("Champ Secret introuvable");
}
Attention ! N'abusez pas de l'accès aux champs privés — c'est casser l'encapsulation et peut provoquer des bugs.
Obtenir et modifier une propriété
PropertyInfo pi = t.GetProperty("Name");
pi.SetValue(p, "John");
Console.WriteLine(pi.GetValue(p)); // John
Les propriétés ne sont pas des champs, ce sont des méthodes get/set derrière le rideau.
Pourquoi ça ne marche parfois pas ?
Si votre champ ou propriété est privé, assurez-vous d'ajouter les flags BindingFlags appropriés (par exemple NonPublic). Si une propriété n'a pas de setter, vous ne pourrez rien y écrire via la réflexion — la règle s'applique.
5. Appel de méthodes par nom
Appel d'une méthode simple sans paramètres
public class Greeter
{
public void SayHello()
{
Console.WriteLine("Hello!");
}
}
Greeter g = new Greeter();
Type t = g.GetType();
MethodInfo mi = t.GetMethod("SayHello");
mi.Invoke(g, null); // Hello!
Appel d'une méthode avec paramètres
public class MathOp
{
public int Add(int x, int y) => x + y;
}
MathOp calc = new MathOp();
Type t = calc.GetType();
MethodInfo mi = t.GetMethod("Add");
object result = mi.Invoke(calc, new object[] { 7, 5 });
Console.WriteLine(result); // 12
Si la méthode est static, passez null comme premier argument de Invoke.
Travailler avec les méthodes overloadées
MethodInfo mi = t.GetMethod(
"Add",
new Type[] { typeof(int), typeof(int) }
);
Ou bien itérez et filtrez manuellement :
foreach(var method in t.GetMethods()) {
if (method.Name == "Add" && method.GetParameters().Length == 2)
{
// ... notre méthode !
}
}
6. Nuances utiles
Membres principaux, méthodes et propriétés pour analyser un type
| Membre de classe | Méthode pour obtenir l'info | Classe résultat | Comment lire/modifier la valeur |
|---|---|---|---|
| Champ (field) | |
|
|
| Propriété (prop) | |
|
|
| Méthode (method) | |
|
|
| Constructeur | |
|
|
Pourquoi tout ça est utile en pratique ?
- Dans les ORM (par ex. Entity Framework construit des requêtes SQL à partir de vos modèles).
- Dans les systèmes de sérialisation et les parsers (par ex. les serializers JSON).
- Dans les bibliothèques génériques pour logging, testing, générateurs UI.
- Aux entretiens — ils aiment demander “comment écririez-vous un copyeur universel d'objet ?”.
- Pour écrire des plugins, quand votre appli charge et appelle du code écrit par d'autres.
Même si ça vous semble être de la magie digne d'un film de hackers, croyez-moi — ça servira !
7. Erreurs typiques et particularités
Les développeurs se trompent souvent avec BindingFlags — ils ne voient pas les champs privés ou, au contraire, obtiennent trop de méthodes. Vérifiez toujours la combinaison de flags : pour les champs privés utilisez NonPublic, pour les statiques — Static, etc.
Quand vous appelez des méthodes via Invoke, passez les arguments dans le même ordre et avec les mêmes types que dans la déclaration. Un ordre/type incorrect mènera à TargetParameterCountException ou ArgumentException. Le résultat retourné arrive comme object — n'oubliez pas de caster le type (ou d'utiliser le pattern matching).
N'oubliez pas les performances : la réflexion est plus lente que les appels directs. Dans les endroits chauds, mettez en cache les Type/MethodInfo/PropertyInfo trouvés ou générez des delegates à l'avance.
Vous verrez souvent des méthodes utilitaires comme get_PropertyName et set_PropertyName. Ce sont les méthodes d'accès aux propriétés — pas besoin de les créer manuellement.
Si vous modifiez des champs ou propriétés privées — vous brisez l'encapsulation. Une mauvaise utilisation peut provoquer des bugs difficiles à traquer. Comme disait l'oncle Ben : “Avec un grand pouvoir vient une grande responsabilité” — utilisez avec prudence, surtout dans les projets de bibliothèque.
GO TO FULL VERSION