11

私は持っていIEnumerable<T>ます。コレクションの各アイテムに対して 1 つのことを行いたいのですが、最後のアイテムを除いて、別のことをしたいと考えています。これをきれいにコーディングするにはどうすればよいですか?擬似コードで

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>](not IList[<T>]etc)、カウントなどに頼ることはできません:したがって、展開したくなるでしょう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

マークの答えに似ていますが、それをまとめる拡張メソッドを書くことができます。

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

100%確かではありませんが、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 に答える
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 に答える