如果采用功能性方法,您可以这样实现一个前瞻枚举器:
IEnumerable<Item> collection = ...;
var lookahead = collection.Zip(collection.Skip(1), Tuple.Create);
枚举器将遍历每个项目及其后续项目的元组。这不包括集合中的最后一个项目。然后只需要执行查询即可。
var query = collection.Zip(collection.Skip(1), Tuple.Create)
.Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null)
.Select(tuple => tuple.Item1);
很遗憾,这种方式非常低效。您正在两次枚举集合的长度,这可能非常昂贵。
最好编写自己的枚举器,这样您只需一次通过集合即可:
public static IEnumerable<TResult> LookAhead<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TSource, TResult> selector)
{
if (source == null) throw new ArugmentNullException("source");
if (selector == null) throw new ArugmentNullException("selector");
using (var enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
{
yield break;
}
var current = enumerator.Current;
while (enumerator.MoveNext())
{
var next = enumerator.Current;
yield return selector(current, next);
current = next;
}
}
}
然后查询变成了:
var query = collection.LookAhead(Tuple.Create)
.Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null)
.Select(tuple => tuple.Item1)
Kind
是null
,那么list[i + 1]
将会超出列表的索引范围。 - nemesvElementAtOrDefault
和ElementAt
中用i+1
替换i
以使其正确。 - nemesvElementAtOrDefault
还是ElementAt
调用都会再次遍历列表(每个都是O(n)
而不是O(1)
),使得这个过程比应该花费更多时间。如果性能是一个问题,我会避免在这种情况下使用 LINQ 并选择一个更标准的方法。 - Adi LesterElementAt
和ElementAtOrDefault
方法仅在不实现IList<T>
时迭代所有元素。如果它们实现了IList<T>
接口,则该项将在常数时间(O(1)
)内返回。 - Alex EssilfieElementAtOrDefault
,解决方案仍然适用。我确实同意,仅出于兴趣而使用LINQ可能不是最灵活/可读的方法,但它确实可以做到。 - Ocelot20