实现LINQ中的TakeLast操作符

C#中,LINQ(Language Integrated Query)提供了一种强大的查询语言,用于操作数据集合。TakeLast操作符是LINQ中一个非常有用的扩展方法,它允许从序列的末尾获取指定数量的连续元素。本文将介绍如何手动实现这个操作符,并探讨一些优化技巧。

TakeLast操作符的基础实现

实现TakeLast操作符的第一步是创建一个缓冲区,用于存储来自源序列的元素。这个缓冲区的大小由参数count决定,并且可以作为一个循环缓冲区来使用。以下是实现TakeLast操作符的基础代码:

public static IEnumerable TakeLast(this IEnumerable source, int 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; } if (numOfItems < count) { for (int 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]; } }

在上述代码中,首先使用一个for循环来填充缓冲区。如果缓冲区中的元素数量少于请求的数量,就返回所有缓冲区中的元素。接下来,通过循环缓冲区的方式迭代源序列的剩余元素。最后,迭代缓冲区中的元素并返回它们。

优化技巧

实现TakeLast操作符时,可以进行一些优化以提高效率。以下是两种常见的优化方法:

如果请求的元素数量为0,可以直接返回一个空序列,而不需要进行任何迭代。这可以通过以下代码实现:

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

这段代码检查count参数是否小于或等于0,如果是,则直接返回一个空的序列。

如果源序列实现了IList接口,可以利用这个接口的特性来进一步优化。IList接口提供了Count属性和索引访问,这使得可以更高效地生成最终序列。以下是优化后的代码:

if (source is IList list) { int listCount = list.Count; for (int i = listCount - ((count < listCount) ? count : listCount); i < listCount; i++) { yield return list[i]; } }

在这段代码中,首先检查源序列是否实现了IList接口。如果是,使用Count属性和索引访问来生成最终序列。这样可以避免使用循环缓冲区,从而提高效率。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485