11

我有一个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()就像循环整个事情一样昂贵。

4

5 回答 5

22

既然你提到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"
    }
}
于 2012-05-24T10:38:12.457 回答
2

类似于 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);
    }
}
于 2013-02-19T02:36:09.810 回答
1

如果您想尽可能高效地执行此操作,除了有效地查看当前项目而且还查看“下一个”或“上一个”项目之外别无选择,因此您可以在获得该信息后推迟决定要做什么. 例如,假设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);
}

看到它在行动

于 2012-05-24T10:41:05.050 回答
0

我不会真的推荐它,但我想你可以做这样的事情......

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
于 2012-05-24T10:43:44.877 回答
0

不是 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);
于 2012-05-24T10:46:58.253 回答