creating generic object or generic method with reflection.

Some times i am in a situation that i needed to create a generic Type or a generic Method with a changing type at runtime. I will start with the generation of a generic method. Lets asume i have a method ConvertValue defined in a class Converter:

public class Converter
{
    public TResult ConvertValue<TResult>(int value)
    {            
        TResult resultValue = (TResult)Convert.ChangeType(value, typeof(TResult));
        return resultValue;         
    }
}
var intValue = 10;
Converter converter = new Converter();
double doubleValue = converter.ConvertValue(intValue);

As we see that method converts an int value to a generic type, in our case in a double. But what is if we dont know the type we want to convert in the intValue at compile type. What if the type is choosen at runtime. Then we need the MakeGenericMethod method from the System.Reflection namespace. That call builds a generic method and then we can invoke that method with the correct generic type.

public class CreateGenericTypeAndMethod
{
    public static object MakeGenericMethod(object obj, string methodName, 
                                           Type innerType, params object[] args)
    {            
        MethodInfo method = obj.GetType().GetMethod(methodName, 
            BindingFlags.Public | BindingFlags.NonPublic |
            BindingFlags.Instance | BindingFlags.Static);
        MethodInfo generic = method.MakeGenericMethod(innerType);
        return generic.Invoke(obj, args);
    }
}
var intValue = 10;
Type type = typeof(double);
object obj = MakeGenericMethod(new Converter(), "ConvertValue", type, 10);
// obj is now from type double and has the value 10.0
var str = String.Format("{0:0.0}",((double)obj));
Console.WriteLine("obj has the type {0} and the value {1}", obj.GetType(), str);

After the MakeGenericMethod call the obj object is now from type double and has the value 10.0 .
objAsDouble
But what is if you want to generate a class with a generic type T. In the next example i show how to generate a Stack of type T. The type T is not known at compile type but at runtime.

public class Stack<T>
{
    private List<T> list = new List<T>();

    public void Push(T value)
    {
        list.Add(value);
    }

    public T Pop()
    {
        if (list.Count > 0)
        {
            T value = list[list.Count - 1];
            list.RemoveAt(list.Count - 1);
            return value;
        }
        return default(T);
    }
}
public class CreateGenericTypeAndMethod
{
    public static object MakeGenericType(Type generic, Type innerType, 
        params object[] args)
    {
        Type specificType = generic.MakeGenericType(new Type[] { innerType });
        return Activator.CreateInstance(specificType, args);
    }
}
Type type = typeof(double);
object stack = CreateGenericTypeAndMethod.MakeGenericType
    (typeof(Stack<>), type, new object[] { });

Here i generate a Stack for double values.

stackAsDouble

I call the MakeGenericType method with the type of the object (Stack) and the type of the generic type (double). In the MakeGenericType method i generate the generic type of the Stack (Stack). For that generation i use the MakeGenericType method of the Type class. With that type i instanciate the object by calling Activator.CreateInstance. That is all i have to do to create the Stack.

Here is the complete implementation of the CreateGenericTypeAndMethod class. There are two overloads for generic type generation and generic methode generation with one generic parameter or multible generic parameters.


public class CreateGenericTypeAndMethod
{
    public static object MakeGenericType(Type generic, Type innerType, 
        params object[] args)
    {
        Type specificType = generic.MakeGenericType(new Type[] { innerType });
        return Activator.CreateInstance(specificType, args);
    }

    public static object MakeGenericType(Type generic, Type[] innerTypes, 
        params object[] args)
    {
        Type specificType = generic.MakeGenericType(innerTypes);
        return Activator.CreateInstance(specificType, args);
    }

    public static object MakeGenericMethod(object obj, string methodName, 
        Type innerType, params object[] args)
    {            
        MethodInfo method = obj.GetType().GetMethod(methodName, 
            BindingFlags.Public | BindingFlags.NonPublic | 
            BindingFlags.Instance | BindingFlags.Static);
        MethodInfo generic = method.MakeGenericMethod(innerType);
        return generic.Invoke(obj, args);
    }

    public static object MakeGenericMethod(object obj, string methodName, 
        Type[] innerTypes, params object[] args)
    {
        MethodInfo method = obj.GetType().GetMethod(methodName, 
            BindingFlags.Public | BindingFlags.NonPublic | 
            BindingFlags.Instance | BindingFlags.Static);
        MethodInfo generic = method.MakeGenericMethod(innerTypes);
        return generic.Invoke(obj, args);
    }
}
Advertisements


If you have a note or a question please write a comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s