Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a generic extension method dynamically?

I wrote this extension method:

public static DataTable ToDataTable<T>(this IList<T> list)
{...}

It works well if called with a type known at compile time:

DataTable tbl = new List<int>().ToDataTable();

But how to call it if the generic type isn't known?

object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work
like image 211
al-bex Avatar asked Feb 28 '10 00:02

al-bex


People also ask

How do you implement and call a custom extension method?

To define and call the extension methodDefine a static class to contain the extension method. The class must be visible to client code. For more information about accessibility rules, see Access Modifiers. Implement the extension method as a static method with at least the same visibility as the containing class.

Why are extension methods static?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

What keyword is used to define an extension method?

Extension Method uses the "this" keyword as the first parameter. The first parameter always specifies the type that the method operates on. The extension method is written inside a static class and the method is also defined as static.


1 Answers

This occurs because a List<int> is not a List<object> -- the List type is not covariant in its element type parameter. Unfortunately you would need to get a typed version of the generic method and call it using reflection:

Type listItemType = typeof(int);   // cheating for simplicity - see below for real approach
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...);
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType));
typedMethod.Invoke(null, new object[] { list });

An alternative may be to create a version of your extension method that accepts IList rather than IList<T>. The List<T> class implements this non-generic interface as well as the generic interface, so you will be able to call:

public static DataTable WeakToDataTable(this IList list) { ... }

((IList)list).WeakToDataTable();

(In reality you'd probably use an overload rather than a different name -- just using a different name to call out the different types.)


More info: In the reflection solution, I skipped over the problem of how to determine the list element type. This can be a bit tricky depending on how sophisticated you want to get. If you're assuming that the object will be a List<T> (for some T) then it's easy:

Type listItemType = list.GetType().GetGenericArguments()[0];

If you're only willing to assume IList<T> then it's a bit harder, because you need to locate the appropriate interface and get the generic argument from that. And you can't use GetInterface() because you're looking for a closed constructed instance of a generic interface. So you have to grovel through all the interfaces looking for one which is an instance of IList<T>:

foreach (Type itf in list.GetType().GetInterfaces())
{
  if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>))  // note generic type definition syntax
  {
    listItemType = itf.GetGenericArguments()[0];
  }
}

This will work for empty lists because it goes off the metadata, not the list content.

like image 186
itowlson Avatar answered Oct 12 '22 21:10

itowlson