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];
如果您只想假设IList
IList
,那么这就有点困难了,因为您需要找到合适的接口并从中获取泛型参数。您不能使用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);
        }            
    }
}