Introduction : quand le code dit « je ne sais pas »
\nImaginez la situation : vous écrivez en C# une bibliothèque de sérialisation. Vous avez une méthode Serialize<T>(T obj) qui sait transformer n'importe quel objet en chaîne. Tout va bien tant que vous l'appelez avec un type spécifique : Serialize<User>(user). Mais que faire si le type de l'objet n'est connu qu'à l'exécution du programme ?
C'est un problème classique que des milliers de développeurs posent sur Stack Overflow : « How do I call a generic method using a Type variable? ». À première vue, la solution semble simple, mais C# est un langage fortement typé, et il n'est pas possible de simplement substituer une variable de type entre les chevrons.
\n\nDans ce tutoriel, nous allons voir comment appeler correctement des méthodes génériques via la réflexion C# à l'aide de MakeGenericMethod. Vous apprendrez non seulement la syntaxe, mais aussi quand cela est vraiment nécessaire et quand il vaut mieux chercher des alternatives.
Pourquoi ne peut-on pas simplement écrire Method<type>() ?
\nDissipons tout de suite les illusions. Le code suivant ne compilera pas :
\nType runtimeType = someObject.GetType();\n// Erreur de compilation : "Le type 'runtimeType' n'est pas valide"\nSomeMethod<runtimeType>();\n\n\nPourquoi ? Parce que les paramètres génériques en C# sont des métadonnées de compilation. Le compilateur doit savoir exactement avec quel type il travaille pour générer le code. La variable Type est un objet qui n'existe qu'à l'exécution. Pour le compilateur, ce n'est qu'une variable, pas un type.
La solution se trouve dans le domaine de la réflexion — un mécanisme qui permet d'inspecter et d'appeler les membres des types pendant l'exécution.
\n\nTutoriel pas à pas : appeler une méthode générique via Type
\nPrenons un exemple concret. Supposons que nous ayons un service de traitement de données :
\npublic class DataProcessor\n{\n public string Process<T>(T data)\n {\n return $"Traitement de {typeof(T).Name}: {data}";\n }\n}\n\n\nNous voulons appeler Process<T> lorsque le type T n'est connu qu'à l'exécution, par exemple via typeof(string) ou someObject.GetType().
Étape 1. Obtenir le MethodInfo pour la méthode générique
\nD'abord, nous devons obtenir un objet représentant notre méthode :
\nDataProcessor processor = new DataProcessor();\nType processorType = typeof(DataProcessor);\n\n// Recherche de la méthode par son nom. Important : spécifier BindingFlags si la méthode n'est pas publique\nMethodInfo? methodInfo = processorType.GetMethod("Process");\n\n\nImportant ! Si vous avez plusieurs surcharges de la méthode, vous devrez affiner la recherche avec GetMethods() et un filtrage. Dans notre cas, il n'y a qu'une seule méthode — c'est simple.
Étape 2. Transformer la méthode générique en méthode concrète avec MakeGenericMethod
\nLe voici, le héros de cet article — la méthode MakeGenericMethod. Elle prend un tableau de types et retourne une méthode construite (concrète) :
Type runtimeType = typeof(int); // ou tout autre type obtenu dynamiquement\nMethodInfo? genericMethod = methodInfo?.MakeGenericMethod(runtimeType);\n\n\nMaintenant, genericMethod est une méthode complète Process<int>, prête à être appelée.
Étape 3. Appeler la méthode via Invoke
\nL'accord final — l'utilisation de Invoke :
object? result = genericMethod?.Invoke(processor, new object[] { 42 });\nConsole.WriteLine(result); // Affichera : "Traitement de Int32: 42"\n\n\nNotez bien :
\n- \n
- Le premier paramètre de
Invokeest l'instance de l'objet (pour les méthodes statiques —null). \n - Le deuxième paramètre est un tableau des arguments de la méthode. Même s'il n'y a qu'un seul argument, il doit être dans un tableau. \n
- Le résultat est retourné sous forme de
object?— vous devrez peut-être \n