我怎么知道我是否正在迭代集合的最后一项?

时间:2010-12-26 20:03:02

标签: c# vb.net collections foreach iteration

我想在我正在迭代的KeyValuePair的最后Dictionary做一些不同的事情。

For Each item In collection
    If ItsTheLastItem
        DoX()
    Else
        DoY()
    End If
Next 

这可能吗?

重新编辑:我没有使用字典值,我实际上使用的是KeyValuePair列表。我转换了它们,后来没有注意到,愚蠢的我。

9 个答案:

答案 0 :(得分:8)

除非你想自己实现foreach,否则请使用计数器(C#代码):

int count = collection.Count;
int counter = 0;

foreach(var item in collection)
{
  counter++;
  if(counter == count)
    DoX();
  else
    DoY();
}

请注意,这仅适用于非流式IEnumerable<T>,同时,根据实施情况,Count会导致集合走两次。

答案 1 :(得分:8)

我会制作你自己的扩展方法。这里的实现保证你只能走一次集合。

static class IEnumerableExtensions {
    public static void ForEachExceptTheLast<T>(
        this IEnumerable<T> source,
        Action<T> usualAction,
        Action<T> lastAction
    ) {
        var e = source.GetEnumerator();
        T penultimate;
        T last;
        if (e.MoveNext()) {
            last = e.Current;
            while(e.MoveNext()) {
                penultimate = last;
                last = e.Current;
                usualAction(penultimate);
            }
            lastAction(last);
        }
    }
}    

用法:

Enumerable.Range(1, 5)
          .ForEachExceptTheLast(
              x => Console.WriteLine("Not last: " + x),
              x => Console.WriteLine("Last: " + x)
);

输出:

Not last: 1
Not last: 2
Not last: 3
Not last: 4
Last: 5

答案 2 :(得分:7)

为什么不使用:

List<string> list = new List<string>();
// add items

foreach (string text in list)
{
    if (text.Equals(list[list.Count - 1]))
    {
        // last item
    }
    else
    {
        // not last item
    }
}

答案 3 :(得分:3)

将集合转换为列表(如LINQ的ToList())并使用for(int i = 0)...循环进行迭代。

答案 4 :(得分:2)

你不能用IEnumerable<T>自然地做到这一点,因为在你试图进入下一个元素之前你不会知道是否还有其他元素。

然而,在我的MiscUtil library我有一个名为SmartEnumerable的东西,它预先读取一个项目,以便提供以下属性:

  • IsFirst
  • IsLast
  • Index
  • Value

有关使用说明,请参阅this page。例如:

For Each item In collection.ToSmartEnumerable()
    If item.IsFirst
        DoX()
    Else
        DoY()
    End If
Next 

您需要使用item.Value来获取当前项的值。

关于迭代字典的一点是 - 它们实际上并不是任何顺序。因此,虽然您可以迭代并且作为第一个条目和最后一个条目,但您不应该假设它们将是添加的第一个条目和分别添加的最后一个条目。对于您的用例而言,这可能不是问题,但您应该了解它。

答案 5 :(得分:2)

我刚才有一个不使用计数器的想法。

For Each item In collection.GetRange(0, collection.Count - 1)
    DoY(item)
Next
DoX(collection.Last)

编辑:抱歉这是一个List,我之前已经转换过它,intellisense给了我List方法而不是Dictionary方法。

答案 6 :(得分:1)

        foreach (var item in collection.Reverse().Skip(1))
        {

        }
        // do here the stuff related to list.LastOrDefaut()

另一种方法是维护迭代索引,当命中collection.Length-2collection.Count()-2时,然后停止循环并使用最后一个元素做任何你想做的事。

但是@BrokenGlass表示要注意它可能对你的应用程序产生的副作用。

通常你必须特别小心使用Linq。 例如,每次迭代linq到sql集合时,你再次执行查询,所以通过简单地调用.ToList()并在内存集合中将其用作 eagerly 来运行它会好得多。你想要的很多。

答案 7 :(得分:1)

我根据上面的@CamiloMartin回答创建了一个从4个八位字节构建IP字符串的解决方案。

 String sIP;
 StringBuilder sb = new StringBuilder();
 List<String> lstOctets= new List<string>{ usrIP1.Text, usrIP2.Text, usrIP3.Text, usrIP4.Text };
 foreach (String Octet in lstOctets.GetRange(0,lstOctets.Count - 1))
 {
    sb.Append(string.Format("{0:d}.", Octet));
 }
 if (lstOctets.Count == 4)
    sb.Append(string.Format("{0:d}", lstOctets[lstOctets.Count - 1]));
 else
     sb.Append(string.Format("{0:d}.0", lstOctets[lstOctets.Count - 1]));
 sIP = sb.ToString();

答案 8 :(得分:0)

使用词典,您有多个选项

Dim dictionary as new Dictionary(of String, MyObject)
For Each item in dictionary
    // item is KeyValue pair
Next

For Each item in dictionary.Keys
    // item is String
Next

For Each item in dictionary.Keys
    // item is MyObject
Next

我建议你对ValueCollection进行itterate,并使用一个计数器或在当前项和最后一项之间进行Equals比较(这可能会慢一点但你保存一个计数器变量;) 不要忘记添加空列表的检查。

For Each item in dictionary.Values

    If dictionary.Count > 0 AndAlso _
        item.Equals(dictionary.Values(dictionary.Count - 1)) Then
        Console.WriteLine("Last Item")
    Else
        Console.WriteLine("Some other Item")
    End If

Next