al-*_*bex 7 c# generics extension-methods
我写了这个扩展方法:
public static DataTable ToDataTable<T>(this IList<T> list)
{...}
Run Code Online (Sandbox Code Playgroud)
如果在编译时使用已知类型调用它,它的效果很好:
DataTable tbl = new List<int>().ToDataTable();
Run Code Online (Sandbox Code Playgroud)
但是如果通用类型未知,如何调用呢?
object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work
Run Code Online (Sandbox Code Playgroud)
ito*_*son 11
发生这种情况是因为a 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 });
Run Code Online (Sandbox Code Playgroud)
另一种方法是创建一个接受IList而不是接受的扩展方法的版本IList<T>.在List<T>类实现此非通用接口以及通用接口,这样你就可以打电话:
public static DataTable WeakToDataTable(this IList list) { ... }
((IList)list).WeakToDataTable();
Run Code Online (Sandbox Code Playgroud)
(实际上你可能使用了一个重载而不是一个不同的名字 - 只是使用一个不同的名称来调用不同的类型.)
更多信息:在反射解决方案中,我跳过了如何确定列表元素类型的问题.根据您想要的复杂程度,这可能有点棘手.如果你假设对象是List<T>(对于某些T),那么很容易:
Type listItemType = list.GetType().GetGenericArguments()[0];
Run Code Online (Sandbox Code Playgroud)
如果你只是愿意假设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];
}
}
Run Code Online (Sandbox Code Playgroud)
这适用于空列表,因为它取消了元数据,而不是列表内容.