IEnumerable <t>和IQueryable <t> </t> </t>的通用方法

时间:2014-07-09 14:06:44

标签: c# generics

我试图制作一个分页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>为什么会出现转换异常?

3 个答案:

答案 0 :(得分:7)

您没有返回列表。 Take是用迭代器块实现的,这意味着实际类型是一个没有编译时标识符的类型(这就是它看起来很奇怪的原因)并且它实现了IEnumerable。它不是List,而是您尝试将其投放到其中。

最重要的是,您的方法无法实际适用于任何IQueryable个对象。它正在调用接受Skip的{​​{1}}的实现(因为通用约束告诉编译器它必须实现的是什么)所以如果它是IEnumerable (即使您解决了混乱的演员问题),您也不会使用查询提供程序来翻译IQueryable电话。你需要在这里有两个重载,一个用于Skip,一个用于IQueryable;鉴于您需要调用两个IEnumerableSkip方法,每个TakeIEnumerable都有一个方法,这就是问题所固有的。

答案 1 :(得分:2)

扩展Servy的(正确)答案 - 另一个问题是SkipTake是扩展方法,这是调用静态方法的语法糖。由于静态方法在编译时被绑定,因此编译器具有的最佳信息是TIEnumerable<TS>,因此编译器将SkipTake绑定到静态方法Enumerable。在运行时无法动态绑定静态方法。这就是IEnumerableIQueryableEnumerableQueryable

上的扩展方法有两个不同的类的原因

您需要两次重载。

答案 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扩展