foreach with generic List,在使用值类型时检测第一次迭代

当通过一个通用列表时,我经常想为列表中的第一个元素做一些不同的事情:

 List objs = new List { new Object(), new Object(), new Object(), new Object() }; foreach (object o in objs) { if (o == objs.First()) { System.Diagnostics.Debug.WriteLine("First object - do something special"); } else { System.Diagnostics.Debug.WriteLine("object Do something else"); } } 

这将输出:

    第一个对象 - 做一些特别的事
    对象做其他事情
    对象做其他事情
    对象做其他事情

这一切都很好,花花公子。

但是,如果我的通用列表是值类型,则此方法将失败。

 List ints = new List { 0, 0, 0, 0 }; foreach (int i in ints) { if (i == ints.First()) { System.Diagnostics.Debug.WriteLine("First int - do something special"); } else { System.Diagnostics.Debug.WriteLine("int Do something else"); } } 

这将输出:

    第一个int  - 做一些特别的事情
    第一个int  - 做一些特别的事情
    第一个int  - 做一些特别的事情
    第一个int  - 做一些特别的事情

现在我知道我可以重新编码这个以添加一个boolean标志变量或传统的for循环,但我想知道是否有任何方法可以找出foreach循环是否在其循环的第一次迭代中。

好吧,您可以使用显式迭代对其进行编码:

 using(var iter = ints.GetEnumerator()) { if(iter.MoveNext()) { // do "first" with iter.Current while(iter.MoveNext()) { // do something with the rest of the data with iter.Current } } } 

bool标志选项(使用foreach )可能更容易了……这就是我(几乎)总是这样做的!

另一种选择是LINQ:

 if(ints.Any()) { var first = ints.First(); // do something with first } foreach(var item in ints.Skip(1)) { // do something with the rest of them } 

上面的缺点是它试图查看列表3次…因为我们知道它是一个列表,这很好 – 但如果我们只有一个IEnumerable ,那么迭代只会是明智的它一次(因为源可能不会重新读取)。

前一段时间我写了SmartEnumerable (MiscUtil的一部分),它让你知道当前元素是第一个还是最后一个,以及它的索引。 这可能对你有帮助……它是MiscUtil的一部分,它是开源的 – 你当然可以在相同的许可下使用SmartEnumerable。

示例代码(来自网页的c’n’p):

 using System; using System.Collections.Generic; using MiscUtil.Collections; class Example { static void Main(string[] args) { List list = new List(); list.Add("a"); list.Add("b"); list.Add("c"); list.Add("d"); list.Add("e"); foreach (SmartEnumerable.Entry entry in new SmartEnumerable(list)) { Console.WriteLine ("{0,-7} {1} ({2}) {3}", entry.IsLast ? "Last ->" : "", entry.Value, entry.Index, entry.IsFirst ? "<- First" : ""); } } } 

编辑:请注意,虽然它适用于具有不同引用的引用类型,但如果您给它一个列表,其中第一个引用在列表中的其他位置出现,它仍然会失败。

 foreach(int i in ints.Take(1)) { //do first thing } foreach(int i in ints.Skip(1)) { //do other things } 

既然您已经在使用LINQ,那么您可以这样做:

 var list = new List(); // do something with list.First(); foreach (var item in list.Skip(1)) { // do other stuff } 

您遇到的问题是因为值类型的相等性是基于实际值,而不是引用本身。 具体来说,在您的示例中,您的列表全为零,因此它询问currentItem == firstItem,并且由于它们都为零,因此总是如此。 对于这种事情,我总是使用布尔标志。

而不是if (i == ints.First()) ,如果你使用if (i.Equals(ints.First())是否有效?

如果你真的不想使用布尔值,你可以这样做:

 List ints = new List { 0, 0, 0, 0 }; System.Diagnostics.Debug.WriteLine("First int - do something special" + ints.FirstOrDefault().ToString()); foreach (int i in ints.Skip(1)) { { System.Diagnostics.Debug.WriteLine("int Do something else"); } } 

但那似乎有点……很奇怪:)

从另一个答案编辑方式:

 IList objs = new List { new Object(), new Object(), new Object(), new Object() }; // to access first element: objs[0] // to access last element: objs[objs.Count - 1] System.Diagnostics.Debug.WriteLine("First object - do something special"); foreach (object o in objs.Skip(1)) { System.Diagnostics.Debug.WriteLine("object Do something else"); } 

参考链接:

使用Skip : Enumerable.Skip方法(IEnumerable,Int32)

使用IList : List或IList