Reputation: 686
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
Upvotes: 8
Views: 4408
Reputation: 686
After having trouble to get it working with the IList<T>
interface I solved it using the IList
interface like itowlson proposed. It's a little bit ugly because of the _T method but it works well:
DataTable tbl = ((IList)value).ToDataTable();
public static class Extensions
{
private static DataTable ToDataTable(Array array) {...}
private static DataTable ToDataTable(ArrayList list) {...}
private static DataTable ToDataTable_T(IList list) {...}
public static DataTable ToDataTable(this IList list)
{
if (list.GetType().IsArray)
{
// handle arrays - int[], double[,] etc.
return ToDataTable((Array)list);
}
else if (list.GetType().IsGenericType)
{
// handle generic lists - List<T> etc.
return ToDataTable_T(list);
}
else
{
// handle non generic lists - ArrayList etc.
return ToDataTable((ArrayList)list);
}
}
}
Upvotes: 0
Reputation: 74802
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.
Upvotes: 12