Friday, October 03, 2008

Invoke Generic Method Dynamically In .NET

Generic method was first introduced in .NET 2.0, and we can write method with dynamic types like:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

class Test
{
public class Foo
{
public string Name { get; set; }
public Foo(string name)
{
Name = "Foo" + name;
}
}
public static List<T> ParseValues<T>(string[] data)
{
List<T> values = new List<T>();
Type type = typeof(T);
if (type == typeof(int))
{
foreach (string str in data)
values.Add((T)((object)int.Parse(str)));
}
else if (type == typeof(double))
{
foreach (string str in data)
values.Add((T)((object)double.Parse(str)));
}
else if (type == typeof(Foo))
{
foreach (string str in data)
values.Add((T)((object)new Foo(str)));
}
return values;
}

static void Main()
{
string[] values = new string[] { "1", "2", "3" };
List<int> numbers = ParseValues<int>(values);
List<double> doubles = ParseValues<double>(values);
List<Foo> foos = ParseValues<Foo>(values);

//Print 1 2 3 1 2 3 Foo1 Foo2 Foo3
numbers.ForEach(i => Console.Write(i + " "));
doubles.ForEach(i => Console.Write(i + " "));
foos.ForEach(i => Console.Write(i.Name + " "));

Console.Read();
}
}
In order to invoke a generic method you have to declare the type statically, and it will be initiated at compile time. Is there a way to get a generic type dynamically? We can do something like:
    Type genericListType = typeof(List<>).MakeGenericType(typeof(double));
var list = Activator.CreateInstance(genericListType);
But that doesn't resolve our problem because we can't determine the T type at compile time to invoke the method of public static List<T> ParseValues<T>(string[] data).

One way I can think of is use the reflection and invoke the method dynamically:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

class Test
{
public class Foo
{
public string Name { get; set; }
public Foo(string name)
{
Name = "Foo" + name;
}
}
public static List<T> ParseValues<T>(string[] data)
{
List<T> values = new List<T>();
Type type = typeof(T);
if (type == typeof(int))
{
foreach (string str in data)
values.Add((T)((object)int.Parse(str)));
}
else if (type == typeof(double))
{
foreach (string str in data)
values.Add((T)((object)double.Parse(str)));
}
else if (type == typeof(Foo))
{
foreach (string str in data)
values.Add((T)((object)new Foo(str)));
}
return values;
}

static void Main()
{
string[] values = new string[] { "1", "2", "3" };
List<int> numbers = ParseValues<int>(values);
List<double> doubles = ParseValues<double>(values);
List<Foo> foos = ParseValues<Foo>(values);

//Print 1 2 3 1 2 3 Foo1 Foo2 Foo3
numbers.ForEach(i => Console.Write(i + " "));
doubles.ForEach(i => Console.Write(i + " "));
foos.ForEach(i => Console.Write(i.Name + " "));
Console.WriteLine();

Type numberType = typeof(int);
MethodInfo genericMethod = typeof(Test).GetMethod("ParseValues").MakeGenericMethod(numberType);
var dynamicList = genericMethod.Invoke(null, new object[] { values });

// Print 1 2 3
foreach (var item in dynamicList as IEnumerable)
Console.Write(item + " ");

Type fooType = typeof(Foo);
genericMethod = typeof(Test).GetMethod("ParseValues").MakeGenericMethod(fooType);
dynamicList = genericMethod.Invoke(null, new object[] { values });

// Print Foo1 Foo2 Foo3
foreach (var item in dynamicList as IEnumerable)
Console.Write((item as Foo).Name + " ");

Console.Read();
}
}
Bear in mind that using Reflection is costly and it would affect performance significantly.