Foreach loop กำหนดว่าการวนซ้ำครั้งสุดท้ายของลูปคืออะไร


233

ฉันมีforeachลูปและจำเป็นต้องดำเนินการตรรกะบางอย่างเมื่อเลือกรายการสุดท้ายจากList, เช่น:

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

ฉันจะรู้ได้อย่างไรว่าลูปใดที่ล่าสุดโดยไม่ใช้ลูปและตัวนับ


1
ลองดูที่คำตอบของฉันที่นี่เพื่อดูคำตอบที่ฉันโพสต์ไว้ในคำถามที่เกี่ยวข้อง
Brian Gideon

คำตอบ:


294

หากคุณเพียงแค่ต้องทำอะไรกับองค์ประกอบสุดท้าย (เมื่อเทียบกับบางสิ่งบางอย่างที่แตกต่างกันกับองค์ประกอบสุดท้ายแล้วใช้ 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
    }
}

แม้ว่าคุณอาจจะต้องเขียน Comparer Last()ที่กำหนดเองเพื่อให้แน่ใจว่าคุณสามารถบอกได้ว่ารายการที่เป็นเช่นเดียวกับรายการที่ส่งกลับโดย

ควรใช้วิธีการนี้ด้วยความระมัดระวังเนื่องจากLastอาจต้องทำซ้ำในการรวบรวม แม้ว่านี่อาจจะไม่ใช่ปัญหาสำหรับคอลเล็กชั่นเล็ก ๆ แต่ถ้ามันใหญ่ขึ้นมันอาจมีผลกระทบต่อประสิทธิภาพ มันจะล้มเหลวหากรายการมีรายการที่ซ้ำกัน ในกรณีนี้สิ่งนี้อาจเหมาะสมกว่า:

int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
    Item result = Model.Results[count];

    // do something with each item
    if ((count + 1) == totalCount)
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}

1
สิ่งที่ฉันต้องการคือ: เมื่อลูปกำลังจะผ่านรายการสุดท้าย: foreach (รายการผลลัพธ์ใน Model.Results) {ถ้า (ผลลัพธ์ == Model.Results.Last ()) {<div> ล่าสุด </div>; } ดูเหมือนว่าคุณหมายถึงสิ่งเดียวกัน
อุบัติเหตุ

10
รหัสของคุณจะวนซ้ำสองครั้งตลอดทั้งชุด - ไม่ดีถ้าชุดไม่เล็ก ดูนี้คำตอบ
Shimmy Weitzhandler

54
วิธีนี้ใช้ไม่ได้หากคุณมีรายการซ้ำในคอลเลกชันของคุณ ตัวอย่างเช่นหากคุณกำลังทำงานกับคอลเลกชันของสตริงและมีการทำซ้ำใด ๆ แล้วรหัส "แตกต่างกับรายการสุดท้าย" จะดำเนินการสำหรับทุกครั้งที่เกิดรายการสุดท้ายในรายการ
muttley91

7
คำตอบนี้เก่า แต่สำหรับคนอื่นที่ดูคำตอบนี้คุณสามารถรับองค์ประกอบสุดท้ายและให้แน่ใจว่าคุณไม่ต้องวนซ้ำผ่านองค์ประกอบโดยใช้: รายการสุดท้าย = แบบจำลองผลตอบแทน [แบบจำลองมาตรฐานจำนวน - 1] จำนวน คุณสมบัติของรายการไม่จำเป็นต้องวนซ้ำ หากคุณมีรายการซ้ำในรายการของคุณให้ใช้ตัวแปรตัววนซ้ำในการวนซ้ำ ปกติเก่าสำหรับลูปไม่เลว
Michael Harris

ฉันแนะนำให้ใช้var last = Model.Result[Model.Result.Count - 1];เร็วกว่าการใช้Last()
Tân

184

แล้วเรื่องความล้าสมัยที่ดีสำหรับลูปล่ะ?

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

14
ppl จำนวนมากคิดว่าปัญหาง่าย ๆ เช่นนี้เมื่อ for for loop สามารถทำได้อย่างสมบูรณ์แล้ว : \
แอนดรูว์ฮอฟแมน

วิธีการแก้ปัญหา Linq เป็นที่ชื่นชอบแน่นอนของฉัน! ขอบคุณสำหรับการแบ่งปัน
mecograph

นี่คือคำตอบที่เหมาะสมกว่าคำตอบที่ยอมรับ
Ratul

หมายเหตุสำหรับทุกคนที่ต้องการใช้โซลูชัน LINQ ในชุดของสตริง (หรือประเภทค่า): โดยทั่วไปแล้วจะไม่ทำงานเพราะการเปรียบเทียบ == จะล้มเหลวหากสตริงสุดท้ายในรายการปรากฏขึ้นก่อนหน้าในรายการด้วย มันจะทำงานได้ก็ต่อเมื่อคุณทำงานกับรายการที่รับประกันว่าจะไม่มีสตริงที่ซ้ำกัน
Tawab Wakil

แต่น่าเสียดายที่คุณไม่สามารถใช้วิธีการแก้ปัญหาที่ฉลาดนี้ถ้าเป็นModel.Results IEnumerableคุณสามารถเรียกCount()ก่อนลูป แต่อาจทำให้เกิดการวนซ้ำเต็มของลำดับ
Luca Cremonesi

42

การใช้งาน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();        
  if(last)
  {
    //additional processing for last item
  }
}

ดังนั้นถ้าประเภทคอลเลกชันเป็นประเภทฟังก์ชั่นจะย้ำถึงองค์ประกอบของคอลเลกชันทั้งหมดIList<T>Last()

ทดสอบ

หากคอลเล็กชันของคุณมีการเข้าถึงแบบสุ่ม (เช่นนำไปใช้IList<T>) คุณสามารถตรวจสอบรายการของคุณได้ดังนี้

if(collection is IList<T> list)
  return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps

1
คุณแน่ใจหรือไม่ว่าตัวแจงนับต้องการusingข้อความสั่ง? ฉันคิดว่ามันจำเป็นเฉพาะในกรณีที่วัตถุจัดการทรัพยากรระบบปฏิบัติการ แต่ไม่ใช่สำหรับโครงสร้างข้อมูลที่มีการจัดการ
หมอบลูกแมว

IEnumerator ไม่ได้ใช้ IDisposable ดังนั้นสายที่ใช้กับการรวบรวมเวลาผิดพลาด! +1 สำหรับการแก้ปัญหาส่วนใหญ่เราไม่สามารถใช้เพียงแค่ for foreach แทนเพราะรายการคอลเลกชันที่นับได้จะคำนวณที่รันไทม์หรือลำดับไม่รองรับการเข้าถึงแบบสุ่ม
Saleh


40

ตามที่คริสแสดง Linq จะทำงานได้ เพียงใช้ Last () เพื่อรับการอ้างอิงไปยังตัวสุดท้ายใน enumerable และตราบใดที่คุณไม่ได้ทำงานกับการอ้างอิงนั้นให้ทำรหัสปกติของคุณ แต่ถ้าคุณกำลังทำงานกับการอ้างอิงนั้นทำสิ่งพิเศษของคุณ ข้อเสียของมันคือมันจะเป็น O (N) - ความสมบูรณ์

คุณสามารถใช้ Count () (ซึ่งก็คือ O (1) ถ้า IEnumerable เป็น ICollection ซึ่งเป็นจริงสำหรับ IEnumerables ในตัวส่วนใหญ่) และไฮบริด foreach ของคุณด้วยเคาน์เตอร์:

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

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

  }
}

สวัสดีอันนี้เป็นวิธีที่ดีที่สุดจนถึงตอนนี้! ง่ายและตรงประเด็น วิธีการเขียนโปรแกรมสิ่งหนึ่ง ทำไมเราไม่เลือกและให้ +1 นี้มากขึ้น!
Hanny Setiawan

1
รายการสุดท้ายควรพบเพียงครั้งเดียว ( ส่งเสริมการบันทึกช่วยจำ ) ก่อนforeachบล็อก var lastItem = objList.LastOrDeafault();เช่นนี้ จากด้านในของforeachวงคุณสามารถตรวจสอบได้ด้วยวิธีนี้: f (item.Equals(lastItem)) { ... }. ในคำตอบเดิมของคุณคำตอบobjList.LastOrDefault()จะย้ำกับคอลเลกชันในแต่ละการวนซ้ำ "foreach" ( เกี่ยวข้องกับความซับซ้อนของพหุนาม )
AlexMelw

คำตอบที่ไม่ดี n ^ 2 ความซับซ้อนแทน n
Shimmy Weitzhandler

11

ดังที่ 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; }
    }
}

9

การปรับปรุงคำตอบของ Daniel Wolfยิ่งขึ้นไปอีกคุณสามารถวางซ้อนกันIEnumerableเพื่อหลีกเลี่ยงการวนซ้ำและลูกแกะหลายตัวเช่น:

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

1
คำตอบอื่น ๆ จะไม่ซ้ำแหล่งที่มาหลายครั้งดังนั้นจึงไม่ใช่ปัญหาที่คุณกำลังแก้ไข คุณได้อนุญาตให้ใช้งานอย่างแน่นอนforeachซึ่งเป็นการปรับปรุง
Servy

1
@Servy ฉันหมายความว่า นอกจากการทำซ้ำคำตอบเดียวจากคำตอบดั้งเดิมฉันกำลังหลีกเลี่ยง lambdas
Fabricio Godoy

7

การใช้ตัววนซ้ำไม่ได้จัดเตรียมไว้ คอลเล็กชันของคุณอาจเป็นสิ่งIListที่เข้าถึงได้ผ่านดัชนีใน O (1) ในกรณีนั้นคุณสามารถใช้for-loop ปกติได้:

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

หากคุณทราบจำนวน แต่ไม่สามารถเข้าถึงได้ผ่านดัชนี (ดังนั้นผลลัพธ์คือICollection) คุณสามารถนับตัวเองได้โดยการเพิ่มจำนวนiในforeachร่างกายและเปรียบเทียบกับความยาว

ทั้งหมดนี้ไม่หรูหราอย่างสมบูรณ์แบบ ทางออกของ Chris อาจจะอร่อยที่สุดเท่าที่ฉันเคยเห็นมา


ในการเปรียบเทียบประสิทธิภาพของตัวนับของคุณภายในโซลูชัน foreach vs Chris ฉันสงสัยว่าอันไหนที่ต้องเสียค่าใช้จ่ายมากกว่าการโทรครั้งสุดท้าย () ครั้งเดียวหรือผลรวมของการดำเนินการที่เพิ่มขึ้นทั้งหมด ฉันสงสัยว่ามันจะใกล้เคียง
TTT

6

สิ่งที่เกี่ยวกับวิธีที่ง่ายกว่าเล็กน้อย

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

2
ฉันไม่รู้ว่าทำไมมีคนโหวตคุณ สิ่งนี้เป็นที่ยอมรับได้อย่างสมบูรณ์เมื่อคุณทำการแสดงหน้าสถานที่แล้วและเกิดต้นทุนของ o (n)
arviman

2
แม้จะมีความจริงที่ว่าคำตอบนั้นสมบูรณ์แบบสำหรับการค้นหาไอเท็มสุดท้าย แต่มันไม่เหมาะสมกับสภาพของ OP " ... ตรวจสอบว่าการทำซ้ำครั้งสุดท้ายของลูปคืออะไร" ดังนั้นคุณไม่สามารถระบุได้ว่าการทำซ้ำครั้งล่าสุดเป็นจริงครั้งสุดท้ายและดังนั้นคุณไม่สามารถจัดการได้แตกต่างกันหรือแม้แต่เพิกเฉย นั่นคือเหตุผลที่มีคนลงคะแนนคุณ @arviman คุณอยากรู้เกี่ยวกับมัน
AlexMelw

1
ถูกต้องฉันพลาดไปทั้งหมด @ Andrey-WD ฉันเดาวิธีแก้ปัญหาคือโทร "last" หนึ่งครั้งก่อน loop (ไม่สามารถทำมันได้ใน loop เพราะมันจะเป็น O (N ^ 2) จากนั้นตรวจสอบว่าการอ้างอิงตรงกับมันหรือไม่
arviman

5

วิธีที่ดีที่สุดอาจเป็นเพียงการดำเนินการขั้นตอนนั้นหลังจากวนรอบ: เช่น

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

3

คำตอบที่ได้รับการยอมรับจะไม่สามารถทำงานซ้ำซ้อนในการรวบรวม หากคุณตั้งค่าไว้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++;
}


1

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

1

ทำการปรับเปลี่ยนเล็กน้อยเพื่อรหัส excel ของ 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

        }
    }
}

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

1

เพียงแค่เก็บค่าก่อนหน้าและทำงานกับมันภายในลูป จากนั้นในตอนท้ายค่า 'ก่อนหน้า' จะเป็นรายการสุดท้ายเพื่อให้คุณจัดการได้แตกต่างกัน ไม่จำเป็นต้องมีการนับหรือไลบรารีพิเศษ

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

1

คุณสามารถใช้ for for loop และไม่จำเป็นต้องเพิ่มส่วนเสริมifภายในforร่างกาย:

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

-1ในforสภาพที่จะดูแลการกระโดดข้ามรายการสุดท้าย


-1 in for for loop ไม่สนใจที่จะข้ามรายการสุดท้าย คุณจะได้รับ IndexOutOfRangeException หากคุณไม่ได้รวม -1
Jaa H


0

หากต้องการทำสิ่งเพิ่มเติมสำหรับแต่ละองค์ประกอบยกเว้นสำหรับวิธีสุดท้ายสามารถใช้วิธีการตามฟังก์ชันได้

delegate void DInner ();

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

วิธีการนี้มีข้อเสียที่ชัดเจน: ความชัดเจนของรหัสน้อยลงสำหรับกรณีที่ซับซ้อนมากขึ้น การโทรผู้รับมอบสิทธิ์อาจไม่มีประสิทธิภาพมาก การแก้ไขปัญหาอาจไม่ง่ายนัก ด้านสว่าง - การเขียนโค้ดสนุก!

ต้องบอกว่าฉันขอแนะนำให้ใช้ธรรมดาสำหรับลูปในกรณีเล็ก ๆ น้อย ๆ ถ้าคุณรู้ว่าการนับคอลเลกชันของคุณไม่ช้าชะมัด


0

อีกวิธีหนึ่งที่ฉันไม่เห็นโพสต์คือการใช้คิว มันคล้ายกับวิธีการใช้วิธีการ 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).
    });

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

                }

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

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


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

(!) ไม่ว่ารหัสของคุณจะดีแค่ไหนหรือดีแค่ไหน หากไม่มีคำอธิบายก็มักจะไม่มีค่า
AlexMelw

นอกจากนี้ยังดูเหมือนว่ามีวิธีการออกแบบมากเกินไป
mecograph

-3

คุณสามารถทำสิ่งนี้:

foreach (DataGridViewRow dgr in product_list.Rows)
{
    if (dgr.Index == dgr.DataGridView.RowCount - 1)
    {
        //do something
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.