IEnumerable<T>
。我想为集合中的每一项做一件事,除了最后一项,我想做其他事情。我怎样才能整齐地编码呢?在伪代码中
foreach (var item in collection)
{
if ( final )
{
g(item)
}
else
{
f(item)
}
}
因此,如果我的 IEnumerable 是
Enumerable.Range(1,4)
,我会执行 f(1) f(2) f(3) g(4)。注意。如果我的 IEnumerable 恰好长度为 1,我想要 g(1)。
我的 IEnumerable 碰巧有点蹩脚,使得
Count()
与循环整个事情一样昂贵。
既然你提到了
IEnumerable[<T>]
(不是IList[<T>]
等),我们就不能依赖计数等:所以我很想展开foreach
:
using(var iter = source.GetEnumerator()) {
if(iter.MoveNext()) {
T last = iter.Current;
while(iter.MoveNext()) {
// here, "last" is a non-final value; do something with "last"
last = iter.Current;
}
// here, "last" is the FINAL one; do something else with "last"
}
}
注意,以上内容技术上仅对
IEnuemerable<T>
有效;对于非通用,您需要:
var iter = source.GetEnumerator();
using(iter as IDisposable) {
if(iter.MoveNext()) {
SomeType last = (SomeType) iter.Current;
while(iter.MoveNext()) {
// here, "last" is a non-final value; do something with "last"
last = (SomeType) iter.Current;
}
// here, "last" is the FINAL one; do something else with "last"
}
}
与 Marc 的答案类似,但您可以编写一个扩展方法来包装它。
public static class LastEnumerator
{
public static IEnumerable<MetaEnumerableItem<T>> GetLastEnumerable<T>(this IEnumerable<T> blah)
{
bool isFirst = true;
using (var enumerator = blah.GetEnumerator())
{
if (enumerator.MoveNext())
{
bool isLast;
do
{
var current = enumerator.Current;
isLast = !enumerator.MoveNext();
yield return new MetaEnumerableItem<T>
{
Value = current,
IsLast = isLast,
IsFirst = isFirst
};
isFirst = false;
} while (!isLast);
}
}
}
}
public class MetaEnumerableItem<T>
{
public T Value { get; set; }
public bool IsLast { get; set; }
public bool IsFirst { get; set; }
}
然后这样称呼它:
foreach (var row in records.GetLastEnumerable())
{
output(row.Value);
if(row.IsLast)
{
outputLastStuff(row.Value);
}
}
如果您想尽可能高效地做到这一点,除了有效地查看当前项目以及“下一个”或“上一个”项目之外,别无选择,因此您可以推迟在获得该项目后决定要做什么信息。例如,假设
T
是集合中项目的类型:
if (collection.Any()) {
var seenFirst = false;
T prev = default(T);
foreach (var current in collection) {
if (seenFirst) Foo(prev);
seenFirst = true;
prev = current;
}
Bar(prev);
}
不是 100% 确定我喜欢这个,但你总是可以延迟使用 item ,直到你将一步移入 IEnumerable 数组,这样当你到达最后时你还没有使用最后一个条目。
它避免了强制对枚举器进行计数。
object item = null;
foreach (var a in items)
{
// if item is set then we can use it.
if (item != null)
{
// not final item
f(item);
}
item = a;
}
// final item.
g(item);
我真的不会推荐它,但我想你可以做这样的事情......
object m_item = notPartOfListFlag = new object();
foreach(var item in enumerator){
if(m_item != notPartOfListFlag)
{
//do stuff to m_item;
}
m_item = item;
}
//do stuff to last item aka m_item;
但是我会尝试使用某种集合来公开列表中项目的位置,然后使用
if(collection.IndexOf(item) == collection.Count-1) do stuff
这可能有点太多了,但我认为它很优雅。
为
IEnumerable<T>
创建一个扩展方法,为每种类型的项目采用 Action<T>
,我们可以创建这样的循环:
int sum = 0;
collection.IEnumForEachItem(
firstItem =>
{
//do things for first item
sum = firstItem.SomeProperty;
},
midItem =>
{
doThingsForIntermediate(midItem);
sum += midItem.SomeProperty * 2;
},
lastItem =>
{
if (lastItem.SomeProperty > 10);
sum += 10;
else
sum += lastItem.SomeProperty;
}
);
这是扩展方法(必须在静态类中):
public static void IEnumForEachItem<T>(this IEnumerable<T> items,
Action<T> actionForFirstItem, Action<T> actionForMidItems, Action<T> actionForLastItem)
{
using (IEnumerator<T> enumerator = items.GetEnumerator())
{
if (enumerator.MoveNext())
actionForFirstItem(enumerator.Current);
if (!enumerator.MoveNext())
throw new InvalidOperationException("IEnumerable must have at least 2 items");
T currentItem = enumerator.Current;
while (enumerator.MoveNext())
{
actionForMidItems(currentItem);
currentItem = enumerator.Current;
}
actionForLastItem(currentItem);
}
}
由此可以轻松创建仅采用“第一个和其他”以及“其他和最后”的方法。