Introducción: cuando el código dice «no sé»
\nImagine una situación: está escribiendo una biblioteca de serialización en C#. Tiene un método Serialize<T>(T obj) que puede convertir cualquier objeto en una cadena. Todo funciona perfectamente mientras lo invoca con un tipo específico: Serialize<User>(user). Pero, ¿qué sucede si el tipo del objeto solo se conoce durante la ejecución del programa?
Este es un problema clásico que miles de desarrolladores preguntan en Stack Overflow: «How do I call a generic method using a Type variable?». A primera vista, la solución parece simple, pero C# es un lenguaje fuertemente tipado y no se puede simplemente sustituir una variable de tipo entre los corchetes angulares.
\n\nEn este tutorial, analizaremos cómo invocar correctamente métodos genéricos mediante reflexión en C# con MakeGenericMethod. Aprenderá no solo la sintaxis, sino también cuándo es realmente necesario y cuándo es mejor buscar alternativas.
¿Por qué no se puede simplemente escribir Method<type>()?
\nEliminemos las ilusiones de inmediato. El siguiente código no se compilará:
\nType runtimeType = someObject.GetType();\n// Error de compilación: "El tipo 'runtimeType' no es válido"\nSomeMethod<runtimeType>();\n\n\n¿Por qué? Porque los parámetros genéricos en C# son metadatos de tiempo de compilación. El compilador debe saber exactamente con qué tipo está trabajando para generar el código. La variable Type es un objeto que solo existe en tiempo de ejecución. Para el compilador, es simplemente una variable, no un tipo.
La solución se encuentra en el ámbito de la reflexión: un mecanismo que permite inspeccionar e invocar miembros de tipos durante la ejecución.
\n\nTutorial paso a paso: invocar un método genérico mediante Type
\nAnalicemos el problema con un ejemplo concreto. Supongamos que tenemos un servicio para procesar datos:
\npublic class DataProcessor\n{\n public string Process<T>(T data)\n {\n return $"Procesado {typeof(T).Name}: {data}";\n }\n}\n\n\nQueremos invocar Process<T> cuando el tipo T solo se conoce en tiempo de ejecución, por ejemplo, desde typeof(string) o someObject.GetType().
Paso 1. Obtener el MethodInfo para el método genérico
\nPrimero, necesitamos obtener el objeto que representa nuestro método:
\nDataProcessor processor = new DataProcessor();\nType processorType = typeof(DataProcessor);\n\n// Buscamos el método por nombre. Importante: especificar BindingFlags si el método no es public\nMethodInfo? methodInfo = processorType.GetMethod("Process");\n\n\n¡Importante! Si tiene varias sobrecargas del método, deberá refinar la búsqueda usando GetMethods() y filtrando. En nuestro caso, solo hay un método, así que es simple.
Paso 2. Convertir el método genérico en uno concreto usando MakeGenericMethod
\nAquí está el protagonista del artículo: el método MakeGenericMethod. Toma un arreglo de tipos y devuelve un método construido (concreto):
Type runtimeType = typeof(int); // o cualquier otro tipo obtenido dinámicamente\nMethodInfo? genericMethod = methodInfo?.MakeGenericMethod(runtimeType);\n\n\nAhora, genericMethod es un método completo Process<int>, listo para ser invocado.
Paso 3. Invocar el método mediante Invoke
\nEl toque final: usar Invoke:
object? result = genericMethod?.Invoke(processor, new object[] { 42 });\nConsole.WriteLine(result); // Mostrará: "Procesado Int32: 42"\n\n\nTenga en cuenta:
\n- \n
- El primer parámetro de
Invokees la instancia del objeto (para métodos estáticos,null). \n - El segundo parámetro es un arreglo de argumentos del método. Incluso si solo hay un argumento, debe estar dentro de un arreglo. \n
- El resultado se devuelve como
object?— es posible que necesite convertirlo al tipo esperado. \n