Foreach loop, determine which is the last iteration of the loop
我有一个
1 2 3 4 5 | foreach (Item result in Model.Results) { //if current result is the last item in Model.Results //then do something in the code } |
在不使用for循环和计数器的情况下,我能知道哪个循环是最后一个循环吗?
如果您只需要对最后一个元素做一些事情(而不是对最后一个元素做一些不同的事情),那么使用linq将有助于:
1 2 | Item last = Model.Results.Last(); // do something with last |
如果您需要对最后一个元素做一些不同的事情,那么您需要这样的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 } } |
尽管您可能需要编写一个自定义比较器,以确保您可以知道该项与
这种方法应该谨慎使用,因为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int totalCount = result.Count(); for (int count = 0; count < totalCount; count++) { Item result = Model.Results[count]; count++; // do something with each item if (count == totalCount) { // do something different with the last item } else { // do something different with every item but the last } } |
一个老式的for循环怎么样?
1 2 3 4 5 6 | for (int i = 0; i < Model.Results.Count; i++) { if (i == Model.Results.Count - 1) { // this is the last item } } |
或者使用linq和foreach:
1 2 3 4 5 6 | foreach (Item result in Model.Results) { if (Model.Results.IndexOf(result) == Model.Results.Count - 1) { // this is the last item } } |
正如克里斯所示,LINQ将工作;只需使用LAST()来获取枚举中最后一个的引用,只要您不使用该引用,那么就执行正常的代码,但是如果您正在使用该引用,那么请执行额外的操作。它的缺点是它永远是O(n)-复杂性。
您可以使用count()(如果IEnumerable也是ICollection,则为o(1);对于大多数常见的内置IEnumerable,这是正确的),并将foreach与计数器混合使用:
1 2 3 4 5 6 | var i=0; var count = Model.Results.Count(); foreach (Item result in Model.Results) { if(++i==count) //this is the last item } |
在某些类型上使用
然后,解决方案是使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using (var enumerator = collection.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 } } |
试验
除非集合类型是
1 2 3 4 5 6 7 | foreach (var item in objList) { if(objList.LastOrDefault().Equals(item)) { } } |
如Shimmy所指出的,使用last()可能是一个性能问题,例如,如果集合是Linq表达式的实时结果。为了防止多次迭代,可以使用如下"foreach"扩展方法:
1 2 3 4 5 6 7 8 | var elements = new[] {"A","B","C" }; elements.ForEach((element, info) => { if (!info.IsLast) { Console.WriteLine(element); } else { Console.WriteLine("Last one:" + element); } }); |
扩展方法如下所示(作为额外的奖励,它还将告诉您索引,如果您正在查看第一个元素):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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) { using (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; } } } |
迭代器实现没有提供。您的集合可能是可以通过o(1)中的索引访问的
1 2 3 4 | for(int i = 0; i < Model.Results.Count; i++) { if(i == Model.Results.Count - 1) doMagic(); } |
如果您知道计数,但不能通过索引访问(因此,结果是一个
这一切都不太优雅。克里斯的解决方案可能是迄今为止我见过的最好的。
简单一点的方法怎么样?
1 2 3 4 5 6 7 8 9 10 | 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); |
最好的方法可能是在循环之后执行这个步骤:
1 2 3 4 5 6 | foreach(Item result in Model.Results) { //loop logic } //Post execution logic |
或者如果你需要为最后的结果做些什么
1 2 3 4 5 6 7 8 | foreach(Item result in Model.Results) { //loop logic } Item lastItem = Model.Results[Model.Results.Count - 1]; //Execute logic on lastItem here |
进一步改进丹尼尔·沃尔夫的答案,您可以堆叠在另一个EDCOX1×1上,以避免多次迭代和lambdas,例如:
1 2 3 4 5 6 7 8 9 | 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); } } |
扩展方法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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; } } } |
接受的答案对集合中的重复项无效。如果在
1 2 3 4 5 6 7 8 9 10 11 | 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++; } |
".last()"不适合我,所以我必须这样做:
1 2 3 4 5 6 7 | 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 */ ); |
对jon skeet的优秀代码做一些小的调整,您甚至可以通过允许访问前一项和下一项使其更智能。当然,这意味着您必须提前阅读实现中的1项。出于性能原因,前一项和下一项只保留当前迭代项。就像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | 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<T> 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 (i.e. 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 } } } |
如何转换EDCOX1与0个元素对最后一个元素的反应:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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(); } |
只需存储前一个值并在循环中使用它。然后在最后,"上一个"值将是最后一个项目,允许您以不同的方式处理它。不需要计数或特殊库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | bool empty = true; Item previousItem; foreach (Item result in Model.Results) { if (!empty) { // We know this isn't the last item because it came from the previous iteration handleRegularItem(previousItem); } previousItem = result; empty = false; } if (!empty) { // We know this is the last item because the loop is finished handleLastItem(previousItem); } |
使用Linq和Foreach:
1 2 3 4 5 6 | foreach (Item result in Model.Results) { if (Model.Results.IndexOf(result) == Model.Results.Count - 1) { // this is the last item } } |
网址:https://code.i-harness.com/en/q/7213ce
乔恩·斯基特(JonSkeet)不久前创建了一个
下载:http://www.yoda.arachsys.com/csharp/miscutil/
另一种方法,我没有看到张贴,是使用一个队列。它类似于一种实现skiplast()方法的方法,而不需要进行多次迭代。通过这种方式,您还可以对任何数量的最后一项执行此操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } |
要调用此函数,请执行以下操作:
1 2 3 4 5 | Model.Results.ForEachAndKnowIfLast( (result, isLast) => { //your logic goes here, using isLast to do things differently for last item(s). }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | List<int> ListInt = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int count = ListInt.Count; int index = 1; foreach (var item in ListInt) { if (index != count) { Console.WriteLine("do something at index number " + index); } else { Console.WriteLine("Foreach loop, this is the last iteration of the loop" + index); } index++; } //OR int count = ListInt.Count; int index = 1; foreach (var item in ListInt) { if (index < count) { Console.WriteLine("do something at index number " + index); } else { Console.WriteLine("Foreach loop, this is the last iteration of the loop" + index); } index++; } |
您可以制作一个专门用于此的扩展方法:
1 2 3 4 5 6 7 8 9 | public static class EnumerableExtensions { public static bool IsLast<T>(this List<T> items, T item) { if (items.Count == 0) return false; T last = items[items.Count - 1]; return item.Equals(last); } } |
你可以这样使用它:
1 2 3 4 5 6 7 | foreach (Item result in Model.Results) { if(Model.Results.IsLast(result)) { //do something in the code } } |
您可以只使用for循环,不需要在
1 2 3 | for (int i = 0; i < Model.Results.Count - 1; i++) { var item = Model.Results[i]; } |
在
除了最后一个元素之外,要对每个元素做一些额外的事情,可以使用基于函数的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | delegate void DInner (); .... Dinner inner=delegate { inner=delegate { // do something additional } } foreach (DataGridViewRow dgr in product_list.Rows) { inner() //do something } } |
这种方法有明显的缺点:对于更复杂的情况,代码的清晰度更低。打电话给代表可能不是很有效。故障排除可能不太容易。光明的一面-编码是有趣的!
尽管如此,如果您知道您的集合的计数不是非常慢的话,我建议在一些小的情况下使用plain for循环。
我们可以检查循环中的最后一项。
1 2 3 4 5 6 7 | foreach (Item result in Model.Results) { if (result==Model.Results.Last()) { // do something different with the last item } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | foreach (DataRow drow in ds.Tables[0].Rows) { cnt_sl1 ="" + "<img src='" + drow["images_path"].ToString() +"' alt='' />" + ""; cnt_sl2 ="" + "<p> " + drow["situation_details"].ToString() +" </p>" + ""; if (i == 0) { lblSituationName.Text = drow["situation"].ToString(); } if (drow["images_position"].ToString() =="0") { content +="" + cnt_sl1 + cnt_sl2 +""; cnt_sl1 =""; cnt_sl2 =""; } else if (drow["images_position"].ToString() =="1") { content +="" + cnt_sl2 + cnt_sl1 +""; cnt_sl1 =""; cnt_sl2 =""; } i++; } |
您可以这样做:
1 2 3 4 5 6 7 | foreach (DataGridViewRow dgr in product_list.Rows) { if (dgr.Index == dgr.DataGridView.RowCount - 1) { //do something } } |