这是问题的更一般形式,如何对列表的第一个和最后一个元素进行特殊处理?更具体的问题很容易回答。我们知道第一个元素和最后一个元素的索引,因此我们可以直接访问它们或针对这些值测试索引变量。例如:
for (int i = 0; i < values.Count; ++i)
{
if (i == values.Count - 1)
{
// do something with last element
}
else
{
// do something else
}
}
但是有时我需要用一个变种IEnumerable<T>
。例如:
public static Bar TransformFoo(Foo value)
{
if (isLast /* how do we know this? */)
{
// do something with the last element
}
else
{
// do something else
}
}
public static IEnumerable<Bar> TransformFooSequence(IEnumerable<Foo> source)
{
return source.Select(TransformFoo);
}
由于这是一种常见的模式,所以我想以一种通用的方式解决它(而不是像过去那样为每种情况编写自定义的for循环)。一种选择是使用将序列转换为列表,ToList()
或使用计数元素Count()
。两种情况下的问题都是解决方案涉及评估整个序列,这可能会非常昂贵。
所以问题是,如何在对IEnumerable<T>
序列的第一个和/或最后一个元素进行特殊处理的同时评估序列,同时又以一般的方式保持对该序列的惰性评估呢?
解决此问题的一种方法是为其创建新的扩展方法,IEnumerable<T>
该方法返回源序列的元素以及有关其位置的语义信息。如果源序列的元素具有type T
,则扩展方法将返回type的元组(T, PositionFlags)
。这是代码:
[Flags]
enum PositionFlags
{
None = 0,
First = 1,
Last = 2
}
public static IEnumerable<(T value, PositionFlags flags)> WithPositions<T>(
this IEnumerable<T> source)
{
using (var enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
{
yield break;
}
T value = enumerator.Current;
PositionFlags flags = PositionFlags.First;
while (enumerator.MoveNext())
{
yield return (value, flags);
value = enumerator.Current;
flags = PositionFlags.None;
}
flags |= PositionFlags.Last;
yield return (value, flags);
}
}
然后,我们可以传递位置信息以对序列中的第一项和/或最后一项进行特殊处理。例如:
Bar TransformFoo(Foo value, bool isLast)
{
if (isLast)
{
// do something with the last element
}
else
{
// do something else
}
}
IEnumerable<Bar> TransformFooSequence(IEnumerable<Foo> source)
{
return source
.WithPositions()
.Select(entry => TransformFoo(
entry.value,
(entry.flags & PositionFlags.Last) == PositionFlags.Last));
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句