Foreach循环,确定哪个是循环的最后一个迭代

我有一个foreach循环,当从Listselect最后一个项目时,需要执行一些逻辑,例如:

  foreach (Item result in Model.Results) { //if current result is the last item in Model.Results //then do something in the code } 

我可以知道哪个循环是最后一个,而不使用循环和计数器?

如果你只是需要用最后一个元素做一些事情(而不是与最后一个元素不同的东西,那么使用LINQ将有助于这里:

 Item last = Model.Results.Last(); // do something with last 

如果你需要做一些不同的最后一个元素,那么你需要这样的东西:

 Item last = Model.Results.Last(); foreach (Item result in Model.Results) { // do something with each item if (result.Equals(last)) { // do something different with the last item } else { // do something different with every item but the last } } 

尽pipe您可能需要编写一个自定义比较器来确保您可以知道该项目与Last()返回的项目相同。

这个方法应该谨慎使用,因为Last可能需要遍历集合。 虽然这可能不是一个小集合的问题,如果它变大可能会有性能影响。

如何一个好老式的循环?

 for (int i = 0; i < Model.Results.Count; i++) { if (i == Model.Results.Count - 1) { // this is the last item } } 

或者使用Linq和foreach:

 foreach (Item result in Model.Results) { if (Model.Results.IndexOf(result) == Model.Results.Count - 1) { // this is the last item } } 

如Chris所示,Linq将会工作。 只要使用Last()获得对枚举中最后一个的引用,并且只要你没有使用那个引用,那么执行你的普通代码,但是如果你正在使用那个引用,那么做你的额外的事情。 它的缺点是它总是O(N) – 复杂。

你可以使用Count()(如果IEnumerable也是一个ICollection,那么它就是O(1);对于大多数常见的内置IEnumerables来说,这是正确的),并将你的foreach与一个计数器混合:

 var i=0; var count = Model.Results.Count(); foreach (Item result in Model.Results) { if(++i==count) //this is the last item } 

对某些types使用Last()将循环通过整个集合!
这意味着如果你做了一个foreach并且调用Last() ,你就打了两遍! 我敢肯定你想避免在大型的collections。

那么解决方法是使用do while循环:

 using (var enumerator = .GetEnumerator()) { var last = !enumerator.MoveNext(); T current; while(!last) { current = enumerator.Current; //process item last = !enumerator.MoveNext(); //process item extension according to flag; flag means item } } 

testing

除非集合types是IList<T>types, Last()函数将遍历所有集合元素。

正如Shimmy指出的那样,使用Last()可能是性能问题,例如,如果您的集合是LINQexpression式的实时结果。 为了防止多次迭代,可以使用如下的“ForEach”扩展方法:

 var elements = new[] { "A", "B", "C" }; elements.ForEach((element, info) => { if (!info.IsLast) { Console.WriteLine(element); } else { Console.WriteLine("Last one: " + element); } }); 

扩展方法看起来像这样(作为额外的好处,它也会告诉你索引,如果你正在看第一个元素):

 public static class EnumerableExtensions { public delegate void ElementAction<in T>(T element, ElementInfo info); public static void ForEach<T>(this IEnumerable<T> elements, ElementAction<T> action) { IEnumerator<T> enumerator = elements.GetEnumerator(); bool isFirst = true; bool hasNext = enumerator.MoveNext(); int index = 0; while (hasNext) { T current = enumerator.Current; hasNext = enumerator.MoveNext(); action(current, new ElementInfo(index, isFirst, !hasNext)); isFirst = false; index++; } } public struct ElementInfo { public ElementInfo(int index, bool isFirst, bool isLast) : this() { Index = index; IsFirst = isFirst; IsLast = isLast; } public int Index { get; private set; } public bool IsFirst { get; private set; } public bool IsLast { get; private set; } } } 
 foreach (var item in objList) { if(objList.LastOrDefault().Equals(item)) { } } 

迭代器实现不提供。 您的collections可能是通过O(1)中的索引访问的IList 。 在这种情况下,你可以使用正常for循环:

 for(int i = 0; i < Model.Results.Count; i++) { if(i == Model.Results.Count - 1) doMagic(); } 

如果你知道数,但不能通过索引访问(因此结果是一个ICollection ),你可以通过增加一个iforeach的身体,并将其与长度比较自己。

这一切都不完美。 克里斯的解决scheme可能是迄今为止我见过的最好的解决scheme。

那么简单的方法呢?

 Item last = null; foreach (Item result in Model.Results) { // do something with each item last = result; } //Here Item 'last' contains the last object that came in the last of foreach loop. DoSomethingOnLastElement(last); 

最好的方法可能只是在循环之后执行该步骤:例如

 foreach(Item result in Model.Results) { //loop logic } //Post execution logic 

或者如果你需要做一些最后的结果

 foreach(Item result in Model.Results) { //loop logic } Item lastItem = Model.Results[Model.Results.Count - 1]; //Execute logic on lastItem here 

“.Last()”没有为我工作,所以我不得不这样做:

 Dictionary<string, string> iterativeDictionary = someOtherDictionary; var index = 0; iterativeDictionary.ForEach(kvp => index++ == iterativeDictionary.Count ? /*it's the last item */ : /*it's not the last item */ ); 

被接受的答案将不适用于收集中的重复。 如果你设置在foreach ,你可以添加你自己的索引variables。

 int last = Model.Results.Count - 1; int index = 0; foreach (Item result in Model.Results) { //Do Things if (index == last) //Do Things with the last result index++; } 

对Jon Skeet的优秀代码做一些小的调整,你甚至可以通过访问上一个和下一个项目来使它更聪明。 当然这意味着你必须在实现中读取1个项目。 出于性能原因,仅保留当前迭代项目的上一个和下一个项目。 它是这样的:

 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; // Based on source: http://jonskeet.uk/csharp/miscutil/ namespace Generic.Utilities { /// <summary> /// Static class to make creation easier. If possible though, use the extension /// method in SmartEnumerableExt. /// </summary> public static class SmartEnumerable { /// <summary> /// Extension method to make life easier. /// </summary> /// <typeparam name="T">Type of enumerable</typeparam> /// <param name="source">Source enumerable</param> /// <returns>A new SmartEnumerable of the appropriate type</returns> public static SmartEnumerable<T> Create<T>(IEnumerable<T> source) { return new SmartEnumerable<T>(source); } } /// <summary> /// Type chaining an IEnumerable&lt;T&gt; to allow the iterating code /// to detect the first and last entries simply. /// </summary> /// <typeparam name="T">Type to iterate over</typeparam> public class SmartEnumerable<T> : IEnumerable<SmartEnumerable<T>.Entry> { /// <summary> /// Enumerable we proxy to /// </summary> readonly IEnumerable<T> enumerable; /// <summary> /// Constructor. /// </summary> /// <param name="enumerable">Collection to enumerate. Must not be null.</param> public SmartEnumerable(IEnumerable<T> enumerable) { if (enumerable == null) { throw new ArgumentNullException("enumerable"); } this.enumerable = enumerable; } /// <summary> /// Returns an enumeration of Entry objects, each of which knows /// whether it is the first/last of the enumeration, as well as the /// current value and next/previous values. /// </summary> public IEnumerator<Entry> GetEnumerator() { using (IEnumerator<T> enumerator = enumerable.GetEnumerator()) { if (!enumerator.MoveNext()) { yield break; } bool isFirst = true; bool isLast = false; int index = 0; Entry previous = null; T current = enumerator.Current; isLast = !enumerator.MoveNext(); var entry = new Entry(isFirst, isLast, current, index++, previous); isFirst = false; previous = entry; while (!isLast) { T next = enumerator.Current; isLast = !enumerator.MoveNext(); var entry2 = new Entry(isFirst, isLast, next, index++, entry); entry.SetNext(entry2); yield return entry; previous.UnsetLinks(); previous = entry; entry = entry2; } yield return entry; previous.UnsetLinks(); } } /// <summary> /// Non-generic form of GetEnumerator. /// </summary> IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// <summary> /// Represents each entry returned within a collection, /// containing the value and whether it is the first and/or /// the last entry in the collection's. enumeration /// </summary> public class Entry { #region Fields private readonly bool isFirst; private readonly bool isLast; private readonly T value; private readonly int index; private Entry previous; private Entry next = null; #endregion #region Properties /// <summary> /// The value of the entry. /// </summary> public T Value { get { return value; } } /// <summary> /// Whether or not this entry is first in the collection's enumeration. /// </summary> public bool IsFirst { get { return isFirst; } } /// <summary> /// Whether or not this entry is last in the collection's enumeration. /// </summary> public bool IsLast { get { return isLast; } } /// <summary> /// The 0-based index of this entry (ie how many entries have been returned before this one) /// </summary> public int Index { get { return index; } } /// <summary> /// Returns the previous entry. /// Only available for the CURRENT entry! /// </summary> public Entry Previous { get { return previous; } } /// <summary> /// Returns the next entry for the current iterator. /// Only available for the CURRENT entry! /// </summary> public Entry Next { get { return next; } } #endregion #region Constructors internal Entry(bool isFirst, bool isLast, T value, int index, Entry previous) { this.isFirst = isFirst; this.isLast = isLast; this.value = value; this.index = index; this.previous = previous; } #endregion #region Methods /// <summary> /// Fix the link to the next item of the IEnumerable /// </summary> /// <param name="entry"></param> internal void SetNext(Entry entry) { next = entry; } /// <summary> /// Allow previous and next Entry to be garbage collected by setting them to null /// </summary> internal void UnsetLinks() { previous = null; next = null; } /// <summary> /// Returns "(index)value" /// </summary> /// <returns></returns> public override string ToString() { return String.Format("({0}){1}", Index, Value); } #endregion } } } 

改进丹尼尔·沃尔夫甚至可以进一步回答你可以堆栈在另一个IEnumerable避免多次迭代和lambdaexpression式,如:

 var elements = new[] { "A", "B", "C" }; foreach (var e in elements.Detailed()) { if (!e.IsLast) { Console.WriteLine(e.Value); } else { Console.WriteLine("Last one: " + e.Value); } } 

扩展方法的实现:

 public static class EnumerableExtensions { public static IEnumerable<IterationElement<T>> Detailed<T>(this IEnumerable<T> source) { if (source == null) throw new ArgumentNullException(nameof(source)); using (var enumerator = source.GetEnumerator()) { bool isFirst = true; bool hasNext = enumerator.MoveNext(); int index = 0; while (hasNext) { T current = enumerator.Current; hasNext = enumerator.MoveNext(); yield return new IterationElement<T>(index, current, isFirst, !hasNext); isFirst = false; index++; } } } public struct IterationElement<T> { public int Index { get; } public bool IsFirst { get; } public bool IsLast { get; } public T Value { get; } public IterationElement(int index, T value, bool isFirst, bool isLast) { Index = index; IsFirst = isFirst; IsLast = isLast; Value = value; } } } 

Jon Skeet创build了一个SmartEnumerable<T>types来解决这个问题。 你可以在这里看到它的实现:

http://codeblog.jonskeet.uk/2007/07/27/smart-enumerations/

下载地址: http : //www.yoda.arachsys.com/csharp/miscutil/

除了最后一个元素之外,为了对每个元素进行额外的操作,可以使用基于函数的方法。

 delegate void DInner (); .... Dinner inner=delegate { inner=delegate { // do something additional } } foreach (DataGridViewRow dgr in product_list.Rows) { inner() //do something } } 

这种方法有明显的缺点:对于更复杂的情况,代码更less。 打电话给代表可能不是很有效。 故障排除可能不是很容易。 好的一面 – 编码很有趣!

话虽如此,如果你知道你的集合的数量不是非常慢,我会build议在普通情况下使用plain for循环。

 foreach (DataRow drow in ds.Tables[0].Rows) { cnt_sl1 = "<div class='col-md-6'><div class='Slider-img'>" + "<div class='row'><img src='" + drow["images_path"].ToString() + "' alt='' />" + "</div></div></div>"; cnt_sl2 = "<div class='col-md-6'><div class='Slider-details'>" + "<p>" + drow["situation_details"].ToString() + "</p>" + "</div></div>"; if (i == 0) { lblSituationName.Text = drow["situation"].ToString(); } if (drow["images_position"].ToString() == "0") { content += "<div class='item'>" + cnt_sl1 + cnt_sl2 + "</div>"; cnt_sl1 = ""; cnt_sl2 = ""; } else if (drow["images_position"].ToString() == "1") { content += "<div class='item'>" + cnt_sl2 + cnt_sl1 + "</div>"; cnt_sl1 = ""; cnt_sl2 = ""; } i++; } 

另一种我没有看到的方式是使用Queue。 这与实现SkipLast()方法的方式类似,不需要进行太多的迭代。 这种方式也可以让你在任何数量的最后项目上做到这一点。

 public static void ForEachAndKnowIfLast<T>( this IEnumerable<T> source, Action<T, bool> a, int numLastItems = 1) { int bufferMax = numLastItems + 1; var buffer = new Queue<T>(bufferMax); foreach (T x in source) { buffer.Enqueue(x); if (buffer.Count < bufferMax) continue; //Until the buffer is full, just add to it. a(buffer.Dequeue(), false); } foreach (T item in buffer) a(item, true); } 

要打电话给你,你可以这样做:

 Model.Results.ForEachAndKnowIfLast( (result, isLast) => { //your logic goes here, using isLast to do things differently for last item(s). }); 

如何将foreach转换为最后一个元素:

 List<int> myList = new List<int>() {1, 2, 3, 4, 5}; Console.WriteLine("foreach version"); { foreach (var current in myList) { Console.WriteLine(current); } } Console.WriteLine("equivalent that reacts to last element"); { var enumerator = myList.GetEnumerator(); if (enumerator.MoveNext() == true) // Corner case: empty list. { while (true) { int current = enumerator.Current; // Handle current element here. Console.WriteLine(current); bool ifLastElement = (enumerator.MoveNext() == false); if (ifLastElement) { // Cleanup after last element Console.WriteLine("[last element]"); break; } } } enumerator.Dispose(); } 

我们可以检查循环中的最后一项。

 foreach (Item result in Model.Results) { if (result==Model.Results.Last()) { // do something different with the last item } } 

你可以这样做:

 foreach (DataGridViewRow dgr in product_list.Rows) { if (dgr.Index == dgr.DataGridView.RowCount - 1) { //do something } }