2010-02-28 61 views
6

我写了这个扩展方法:如何动态调用通用扩展方法?

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

为什么你投到'列表'?你的'list'是'List ',演员不会成功。 – Vlad 2010-02-28 00:25:38

+0

因为他在编译时不知道他有什么类型的列表:他不知道它是'List '。他试图通过将其转换为基类来解决该问题(正如你正确地指出的那样,尽管'int'与'object'兼容,那么'List '与列表不兼容) 。 – itowlson 2010-02-28 00:34:26

回答

9

这是因为一个List<int>不是List<object> - 列表类型并不将其元件类型参数协变。不幸的是,你需要得到泛型方法的类型化版本,并使用反射调用它:

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]; 
    } 
} 

这将为空列表的工作,因为它熄灭的元数据,而不是列表内容。

+0

不仅仅是抛出'List '而不是'List '解决问题? – Vlad 2010-02-28 00:36:11

+0

当然,但问题是“如果泛型不知道,怎么调用它**”? (强调增加)。因此,我注意到,实际上,他还必须使用反射来计算listItemType,而不是仅仅假设它是int。 – itowlson 2010-02-28 00:40:19

+0

我试过这个,但我有两个问题:1.如果列表为空,我将如何获取嵌入元素的类型? 2.我有两个ToDataTable()扩展方法。如何获得IList ? – 2010-02-28 15:57:42

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); 
     }    
    } 
}