我写了这个扩展方法:
public static DataTable ToDataTable<T>(this IList<T> list)
{...}
如果在编译时使用已知类型调用它,则效果很好:
DataTable tbl = new List<int>().ToDataTable();
但如果不知道泛型类型,如何调用它?
object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work
答案 0 :(得分:11)
这是因为List<int>
不是List<object>
- List类型在其元素类型参数中不是协变的。不幸的是,您需要获取泛型方法的类型版本并使用反射调用它:
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 });
另一种方法是创建一个接受IList
而不是IList<T>
的扩展方法版本。 List<T>
类实现了这个非泛型接口以及通用接口,因此您可以调用:
public static DataTable WeakToDataTable(this IList list) { ... }
((IList)list).WeakToDataTable();
(实际上你可能会使用重载而不是其他名称 - 只是使用不同的名称来调用不同的类型。)
更多信息:在反射解决方案中,我跳过了如何确定列表元素类型的问题。根据您想要的复杂程度,这可能有点棘手。如果你假设对象是List<T>
(对于某些T),那么很容易:
Type listItemType = list.GetType().GetGenericArguments()[0];
如果你只是愿意假设IList<T>
那么它有点困难,因为你需要找到合适的接口并从中获取泛型参数。而且你不能使用GetInterface(),因为你正在寻找一个通用接口的封闭构造实例。所以你必须浏览所有接口,寻找一个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];
}
}
这适用于空列表,因为它取消了元数据,而不是列表内容。
答案 1 :(得分:0)
在使用IList<T>
界面时遇到问题后,我使用IList
界面解决了问题,如itowlson提议的那样。由于_T方法,它有点难看,但效果很好:
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);
}
}
}