我试图制作一个分页IEnuemrable<T>
和IQueryable<T>
像这样:
public T Paginate<T, TS>(T list) where T : IEnumerable<TS>
{
CheckValidityAndClamp();
return (T)(list.Skip(Page*PageSize).Take(PageSize));
}
当我通过List<int>
实例时,它编译得很好。
但是在运行时它会给出一个强制转换异常:
System.InvalidCastException : An object of type '<TakeIterator>d__3a`1[System.Int32]' can not be converted to the type 'System.Collections.Generic.List`1[System.Int32]'.
为什么? List<int>
实现IEnumerable<int>
为什么会出现转换异常?
答案 0 :(得分:7)
您没有返回列表。 Take
是用迭代器块实现的,这意味着实际类型是一个没有编译时标识符的类型(这就是它看起来很奇怪的原因)并且它实现了IEnumerable
。它不是List
,而是您尝试将其投放到其中。
最重要的是,您的方法无法实际适用于任何IQueryable
个对象。它正在调用接受Skip
的{{1}}的实现(因为通用约束告诉编译器它必须实现的是什么)所以如果它是IEnumerable
(即使您解决了混乱的演员问题),您也不会使用查询提供程序来翻译IQueryable
电话。你需要在这里有两个重载,一个用于Skip
,一个用于IQueryable
;鉴于您需要调用两个IEnumerable
和Skip
方法,每个Take
和IEnumerable
都有一个方法,这就是问题所固有的。
答案 1 :(得分:2)
扩展Servy的(正确)答案 - 另一个问题是Skip
和Take
是扩展方法,这是调用静态方法的语法糖。由于静态方法在编译时被绑定,因此编译器具有的最佳信息是T
是IEnumerable<TS>
,因此编译器将Skip
和Take
绑定到静态方法Enumerable
。在运行时无法动态绑定静态方法。这就是IEnumerable
和IQueryable
(Enumerable
和Queryable
)
您需要两次重载。
答案 2 :(得分:-1)
你得到这个,因为你无法将IEnumerable转换为List(这就是为什么LINQ具有.ToList()扩展名)。我想你想要的是这样的:
public IEnumerable<T> Paginate<T>(IEnumerable<T> list)
{
return list.Skip(Page * PageSize).Take(PageSize);
}
哪个适用于任何实现IEnumerable的源(List,T [],IQueryable)
然后你可以这样称呼它:
IEnumerable<int> list = someQueryResult;
IEnumerable<int> page = class.Paginate<int>(list);
正如其他答案所指出的那样。 IQueryable拥有它自己的一组扩展方法。所以尽管这会起作用,但是分页不会作为数据源完成,因为它将使用IEnumerable扩展