C# 如何动态调用泛型扩展方法?
我写了这个扩展方法:C# 如何动态调用泛型扩展方法?,c#,generics,extension-methods,C#,Generics,Extension Methods,我写了这个扩展方法: public static DataTable ToDataTable<T>(this IList<T> list) {...} 公共静态数据表到数据表(此IList列表) {...} 如果使用编译时已知的类型调用它,则效果良好: DataTable tbl = new List<int>().ToDataTable(); DataTable tbl=new List().ToDataTable(); 但是如果泛型类型未知,如何调
public static DataTable ToDataTable<T>(this IList<T> list)
{...}
公共静态数据表到数据表(此IList列表)
{...}
如果使用编译时已知的类型调用它,则效果良好:
DataTable tbl = new List<int>().ToDataTable();
DataTable tbl=new List().ToDataTable();
但是如果泛型类型未知,如何调用它呢
object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work
对象列表=新列表();
...
tbl=扩展名.ToDataTable((列表)列表);//行不通
发生这种情况是因为列表
不是列表
——列表类型在其元素类型参数中不协变。不幸的是,您需要获取泛型方法的类型化版本,并使用反射调用它:
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
的扩展方法版本。List
类实现此非泛型接口以及泛型接口,因此您可以调用:
public static DataTable WeakToDataTable(this IList list) { ... }
((IList)list).WeakToDataTable();
(实际上,您可能会使用重载而不是不同的名称——只是使用不同的名称来调用不同的类型。)
更多信息:在反射解决方案中,我跳过了如何确定列表元素类型的问题。这可能有点棘手,这取决于你想要的复杂程度。如果您假设对象将是一个
列表
(对于某些T),那么很容易:
Type listItemType = list.GetType().GetGenericArguments()[0];
如果您只想假设IListIList
,那么这就有点困难了,因为您需要找到合适的接口并从中获取泛型参数。您不能使用GetInterface(),因为您正在寻找通用接口的封闭构造实例。因此,您必须仔细查看所有接口,寻找一个是IList
的实例:
foreach(在list.GetType().GetInterfaces()中键入itf)
{
if(itf.IsGenericType&&itf.GetGenericTypeDefinition==typeof(IList))//注意泛型类型定义语法
{
listItemType=itf.GetGenericArguments()[0];
}
}
这将适用于空列表,因为它脱离了元数据,而不是列表内容。在使用
IList
接口时遇到问题后,我使用IList
接口解决了它。由于_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);
}
}
}
DataTable tbl=((IList)值);
公共静态类扩展
{
私有静态数据表ToDataTable(数组){…}
私有静态数据表ToDataTable(ArrayList){…}
私有静态数据表ToDataTable_T(IList列表){…}
公共静态数据表ToDataTable(此IList列表)
{
if(list.GetType().IsArray)
{
//句柄数组-int[],double[,]等。
返回到数据表((数组)列表);
}
else if(list.GetType().IsGenericType)
{
//处理通用列表-列表等。
返回到数据表(列表);
}
其他的
{
//处理非泛型列表-ArrayList等。
返回到数据表((ArrayList)列表);
}
}
}
为什么要强制转换到列表
?你的列表
是一个列表
,演员阵容不会成功。因为他不知道在编译时他得到了什么类型的列表:他不知道它是列表
。他试图通过强制转换到基类来解决这个问题(正如你正确地指出的,这是行不通的,因为List
与List
不兼容,即使int
与object
兼容)。不只是强制转换到List
而不是List
解决问题吗?当然,但问题是“如果泛型类型未知,如何调用它?”(重点补充)。因此我注意到,实际上,他还必须使用反射来计算listItemType,而不仅仅是假设它是int。如果列表为空,如何获取嵌入元素的类型?2.我有两个ToDataTable()扩展方法。如何获得IList的答案?Alex:从你的回答来看,你似乎选择了非通用的IList
解决方案,并使用重载解决了问题,所以我猜你不再需要这两个问题的答案了——如果需要,请留下另一条评论。Alex:我已经更新了答案以回答你的第一个问题。不确定第二个问题--您试图区分哪些扩展方法?
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);
}
}
}