65.9K
CodeProject 正在变化。 阅读更多。
Home

LINQ:实现 TakeLast 运算符。

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0投票)

2010年10月18日

CPOL

1分钟阅读

viewsIcon

11794

TakeLast 运算符从序列的末尾返回指定数量的连续元素,并作为 TakeLast 扩展方法实现。

free hit counters

在上次 文章 之后,在这篇文章中,我将介绍 TakeLast 运算符的实现。

TakeLast 运算符从序列的末尾返回指定数量的连续元素,并作为 TakeLast 扩展方法 实现。

public static IEnumerable<TSource> TakeLast<TSource>(this IEnumerable<TSource> source, int count)

为了实现此运算符,首先我们从 source 序列中缓冲最多 count 个项目到一个数组中,该数组充当循环缓冲区。

var sourceEnumerator = source.GetEnumerator();
var buffer = new TSource[count];
var numOfItems = 0;
int end;

for (end = 0; (end < count) && sourceEnumerator.MoveNext(); end++, numOfItems++)
{
    buffer[end] = sourceEnumerator.Current;
}

如果缓冲的项目数量 (numOfItems) 小于请求的项目数量 (count),我们只需生成所有缓冲的项目。

if (numOfItems < count)
{
    for (idx = 0; idx < numOfItems; idx++)
    {
        yield return buffer[idx];
    }

    yield break;
}

接下来,我们循环遍历剩余的项目,以循环方式缓冲它们。

for (end = 0; sourceEnumerator.MoveNext(); end = (end + 1) % count)
{
    buffer[end] = sourceEnumerator.Current;
}

最后,我们只需循环遍历缓冲的项目并生成它们。

for (; numOfItems > 0; idx = (idx + 1) % count, numOfItems--)
{
    yield return buffer[idx];
}

这里你可以进行两个优化。

第一个显而易见,如果请求的项目数量为 0(零),我们只需返回一个空序列。

if (count <= 0)
{
    return System.Linq.Enumerable.Empty<TSource>();
}

第二个是,如果已知 source 序列实现了 IList<T> 接口。实现此 接口 的对象具有 Count 属性以及对其 项目 的索引访问,这使我们能够优化最终序列的生成。

生成最终序列包括从列表的末尾生成所需的项目数量(或者如果列表包含的项目少于所需数量,则生成所有项目)。

int listCount = list.Count;

for (int i = listCount - ((count < listCount) ? count : listCount); i < listCount; i++)
{
    yield return list[i];
}

你可以在 CodePlex 项目中找到此运算符的完整实现(以及更多):LINQ 工具和运算符:PauloMorgado.Linq

© . All rights reserved.