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

时间:2011-09-19 19:23:31

标签: c# asp.net foreach

我有一个foreach循环,需要在从List中选择最后一项时执行一些逻辑,例如:

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

我可以在不使用for循环和计数器的情况下知道哪个循环是最后的吗?

26 个答案:

答案 0 :(得分:247)

如果你只需要对最后一个元素做一些事情(而不是不同与最后一个元素相比,那么使用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
    }
}

虽然您可能需要编写自定义比较器,以确保您可以告诉该项目与Last()返回的项目相同。

应谨慎使用此方法,因为Last可能必须遍历集合。虽然这对于小型集合来说可能不是问题,但如果它变大,它可能会对性能产生影响。如果列表包含重复项,它也将失败。在这种情况下,这样的事情可能更合适:

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
    }
}

答案 1 :(得分:156)

一个好老式的循环怎么样?

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
     }
}

答案 2 :(得分:36)

克里斯表示,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
 }

答案 3 :(得分:34)

在某些类型上使用Last()将循环播放整个集合!
这意味着,如果你制作一个foreach并致电Last(),那么你就会选择两次!,我相信你不想在大集合中使用。

然后解决方案是使用do while循环:

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

  }
}

Test

除非集合类型的类型为IList<T>,否则Last()函数将遍历所有集合元素。

答案 4 :(得分:18)

foreach (var item in objList)
{
  if(objList.LastOrDefault().Equals(item))
  {

  }
}

答案 5 :(得分:10)

正如Shimmy指出的那样,使用Last()可能是一个性能问题,例如,如果你的集合是LINQ表达式的实时结果。为了防止多次迭代,您可以使用“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) {
        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; }
    }
}

答案 6 :(得分:6)

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

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

如果您知道计数,但无法通过索引访问(因此,结果为ICollection),您可以通过在i的主体中增加foreach来计算自己的数量,将它与长度进行比较。

这一切都不是很优雅。克里斯的解决方案可能是我迄今为止见过的最好的解决方案。

答案 7 :(得分:5)

那么简单的方法呢。

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);

答案 8 :(得分:4)

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

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

答案 9 :(得分:4)

进一步提高Daniel Wolf answer你可以堆叠在另一个IEnumerable上以避免多次迭代和lambdas,例如:

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;
        }
    }
}

答案 10 :(得分:2)

接受的答案不适用于集合中的重复项。如果您在foreach上设置,则可以添加自己的索引变量。

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++;
}

答案 11 :(得分:1)

对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 (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

        }
    }
}

答案 12 :(得分:1)

&#34;。去年()&#34;没有为我工作,所以我不得不做这样的事情:

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 */
);

答案 13 :(得分:1)

只需存储前一个值并在循环中使用它。然后在结束时前面的&#39; value将是最后一项,让您以不同的方式处理它。不需要计数或特殊库。

Cannot resolve symbol Column

答案 14 :(得分:1)

如何转换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();
}

答案 15 :(得分:0)

使用Linq和foreach:

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

答案 16 :(得分:0)

您可以只使用for循环,而无需在if正文中添加额外的for

for (int i = 0; i < Model.Results.Count - 1; i++) {
    var item = Model.Results[i];
}

处于-1条件的for会跳过最后一项。

答案 17 :(得分:0)

您可以创建专用于此的扩展方法:

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);
        }
    }

,您可以像这样使用它:

foreach (Item result in Model.Results)
{
    if(Model.Results.IsLast(result))
    {
        //do something in the code
    }
}

答案 18 :(得分:0)

     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++;

                }

答案 19 :(得分:0)

基于@Shimmy的响应,我创建了一个扩展方法,这是每个人都想要的解决方案。它简单,易用,并且仅循环浏览一次。

internal static class EnumerableExtensions
{
    public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null)
    {
        using var enumerator = collection.GetEnumerator();
        var isNotLast = enumerator.MoveNext();
        while (isNotLast)
        {
            var current = enumerator.Current;
            isNotLast = enumerator.MoveNext();
            var action = isNotLast ? actionExceptLast : actionOnLast;
            action?.Invoke(current);
        }
    }
}

这适用于任何IEnumerable<T>。用法如下:

var items = new[] {1, 2, 3, 4, 5};
items.ForEachLast(i => Console.WriteLine($"{i},"), i => Console.WriteLine(i));

输出如下:

1,
2,
3,
4,
5

此外,您可以将其设置为Select样式的方法。然后,在ForEach中重用该扩展名。该代码如下所示:

internal static class EnumerableExtensions
{
    public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null) =>
        // ReSharper disable once IteratorMethodResultIsIgnored
        collection.SelectLast(i => { actionExceptLast?.Invoke(i); return true; }, i => { actionOnLast?.Invoke(i); return true; }).ToArray();

    public static IEnumerable<TResult> SelectLast<T, TResult>(this IEnumerable<T> collection, Func<T, TResult>? selectorExceptLast = null, Func<T, TResult>? selectorOnLast = null)
    {
        using var enumerator = collection.GetEnumerator();
        var isNotLast = enumerator.MoveNext();
        while (isNotLast)
        {
            var current = enumerator.Current;
            isNotLast = enumerator.MoveNext();
            var selector = isNotLast ? selectorExceptLast : selectorOnLast;
            //https://stackoverflow.com/a/32580613/294804
            if (selector != null)
            {
                yield return selector.Invoke(current);
            }
        }
    }
}

答案 20 :(得分:0)

我没有看到发布的另一种方法是使用队列。它类似于实现SkipLast()方法而不需要迭代的方法。这种方式还允许您对任意数量的最后项目执行此操作。

org.hibernate.cache.CacheException: java.lang.IllegalStateException: The org.hibernate.cache.spi.UpdateTimestampsCache Cache is not alive (STATUS_SHUTDOWN)
at org.hibernate.cache.ehcache.internal.regions.EhcacheGeneralDataRegion.put(EhcacheGeneralDataRegion.java:112)
at org.hibernate.cache.ehcache.internal.regions.EhcacheTimestampsRegion.put(EhcacheTimestampsRegion.java:40)
at org.hibernate.cache.spi.UpdateTimestampsCache.preInvalidate(UpdateTimestampsCache.java:111)
at org.hibernate.engine.spi.ActionQueue.invalidateSpaces(ActionQueue.java:506)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:475)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:347)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:67)
at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1191)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1257)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
at com.resolution.scheduler.dao.hibernate.ReportRunDaoHibernate.getReportRunsByReportScheduleIdAndDate(ReportRunDaoHibernate.java:97)
at com.resolution.scheduler.service.impl.ReportRunManagerImpl.getReportRunsByReportScheduleIdAndDate(ReportRunManagerImpl.java:82)
at sun.reflect.GeneratedMethodAccessor836.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)

要打电话给您,请执行以下操作:

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);
}

答案 21 :(得分:0)

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

delegate void DInner ();

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

这种方法有明显的缺点:对于更复杂的情况,代码清晰度更低。呼叫代表可能不是很有效。故障排除可能不是很容易。光明的一面 - 编码很有趣!

话虽如此,如果你知道你的收藏计数不是非常慢,我会建议在琐碎的情况下使用plain for循环。

答案 22 :(得分:0)

Jon Skeet在前一段时间创建了SmartEnumerable<T>类型以解决此问题。你可以在这里看到它的实现:

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

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

答案 23 :(得分:-1)

我们可以循环查看最后一项。

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

答案 24 :(得分:-2)

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++;
            }

答案 25 :(得分:-3)

你可以这样做:

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