Calling Generic Methods Using Reflection

Full source code here.

Accessing generic methods by reflection is not easy. A while ago I needed to do just that and found relatively little information out there. So I dug in a figured it out for myself.

Here are some of the possible ways that you could execute generic methods by reflection –

  • Execute a Generic Instance Method in a Non-Generic Class
  • Execute Generic Static Method In a Non-Generic Class
  • Execute Generic Overloaded Static Method
  • Execute NonGeneric Method in a Generic Class

There are other variations, but you should be able to figure out how to write them based on the ones shown here.

In the classes PrinterA and PrinterB, there are a variety of generic and one non-generic methods that I will call using reflection from Program.

The code will explain things better than I can, so here is the listing.

using System;
using System.Collections.Generic;

namespace ExecuteGenericMethodByReflection
{
    public class PrinterA<T>
    {
        public void Print(IEnumerable<T> items)
        {
            Console.WriteLine();
            foreach (var item in items)
            {
                Console.Write($"{item} ");
            }
        }
    }
}
using System;
using System.Collections.Generic;

namespace ExecuteGenericMethodByReflection
{
    public class PrinterB
    {
        public void PrintSimple<T>(IEnumerable<T> items)
        {
            Console.WriteLine();

            foreach (var item in items)
            {
                Console.Write($"{item} ");
            }
            Console.WriteLine();

        }

        public static void PrintPrefixAndSuffix<T>(IEnumerable<T> items, string prefix, string suffix)
        {
            Console.Write(prefix);
            foreach (var item in items)
            {
                Console.Write($" {item} ");
            }
            Console.Write(suffix);
            Console.WriteLine();
        }

        //This and the next method are overloads
        public static void PrintPrefix<T>(IEnumerable<T> items, string prefix)
        {
            Console.Write(prefix);

            foreach (var item in items)
            {
                Console.Write($" {item}");
            }
            Console.WriteLine();

        }

        public static void PrintPrefix<T>(IEnumerable<T> items, int prefix)
        {
            Console.WriteLine(prefix);

            foreach (var item in items)
            {
                Console.Write($" {item}");
            }
            Console.WriteLine();
        }

    }
}

And here is program where all the crazy reflection takes place.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace ExecuteGenericMethodByReflection
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();

            List<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };

            p.ExecuteGenericInstanceMethodInNonGenericClass(numbers);
            p.ExecuteGenericStaticMethodInNonGenericClass(numbers);
            p.ExecuteGenericOverloadedStaticMethod(numbers);

            p.ExecuteNonGenericMethodInGenericClass(numbers);

            Console.WriteLine("\n\nPress any key to exit...");
            Console.ReadKey();
        }

        private void ExecuteGenericInstanceMethodInNonGenericClass(object objectToPrint)
        {
            Type typeToPrint = objectToPrint.GetType().GetGenericArguments()[0];

            PrinterB genericPrinter = new PrinterB();
            MethodInfo printSimpleMethod = typeof(PrinterB).GetMethod("PrintSimple");
            MethodInfo printSimpleMethodToExecute = printSimpleMethod.MakeGenericMethod(typeToPrint);

            printSimpleMethodToExecute.Invoke(genericPrinter, new object[] { objectToPrint});
        }

        private void ExecuteGenericStaticMethodInNonGenericClass(object objectToPrint)
        {
            Type typeToPrint = objectToPrint.GetType().GetGenericArguments()[0];
            MethodInfo printPrefixAndSuffixMethod = typeof(PrinterB).GetMethod("PrintPrefixAndSuffix");
            MethodInfo genericprintPrefixAndSuffixMethodToExecute = printPrefixAndSuffixMethod.MakeGenericMethod(typeToPrint);

            genericprintPrefixAndSuffixMethodToExecute.Invoke(null, new object[] { objectToPrint, "My Prefix", "My Suffix" });
        }

        // Executing an overloaded method is more diffcult, the method has to be identified by its parameters
        private void ExecuteGenericOverloadedStaticMethod(object objectToPrint)
        {
            string prefixToPrint = "My Prefix";
            Type overloadType = prefixToPrint.GetType();
            Type typeToPrint = objectToPrint.GetType().GetGenericArguments()[0];
            MethodInfo genricMethodToExecute = GetOverloadedMethod(typeToPrint, overloadType);
            genricMethodToExecute.Invoke(null, new object[] { objectToPrint, prefixToPrint });
        }

        private void ExecuteNonGenericMethodInGenericClass(object objectToPrint)
        {
            Type typeToPrint = objectToPrint.GetType().GetGenericArguments()[0];

            Type printerAType = typeof(PrinterA<>);
            Type genericPrinterAType = printerAType.MakeGenericType(typeToPrint);

            object genericPrinterAInstance = Activator.CreateInstance(genericPrinterAType);
            MethodInfo printMethod = genericPrinterAType.GetMethod("Print");
            printMethod.Invoke(genericPrinterAInstance, new object[] { objectToPrint });
        }

        private MethodInfo GetOverloadedMethod(Type typeToPassToGenericMethod, Type overloadType)
        {
            IEnumerable<MethodInfo> printMethods = typeof(PrinterB).GetMethods().Where(m => m.Name == "PrintPrefix");
            MethodInfo printMethod = printMethods.Single(m => m.GetParameters().Any(p => p.ParameterType == overloadType));
            MethodInfo genericPrintMethod = printMethod.MakeGenericMethod(typeToPassToGenericMethod);
            return genericPrintMethod;
        }
    }
}

Full source code here.

Leave a Reply

Your email address will not be published. Required fields are marked *