Foreach loop, określ, która jest ostatnia iteracja pętli


233

Mam foreachpętlę i muszę wykonać pewną logikę, gdy ostatni element jest wybrany z List, np .:

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

Czy mogę wiedzieć, która pętla jest ostatnia bez użycia pętli i liczników?


1
Spójrz na moją odpowiedź tutaj, aby znaleźć rozwiązanie, które zamieściłem na powiązane pytanie.
Brian Gideon

Odpowiedzi:


294

Jeśli potrzebujesz zrobić coś z ostatnim elementem (w przeciwieństwie do czegoś innego z ostatnim elementem, skorzystanie z LINQ pomoże tutaj:

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

Jeśli chcesz zrobić coś innego z ostatnim elementem, potrzebujesz czegoś takiego:

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

Chociaż prawdopodobnie będziesz musiał napisać niestandardowy moduł porównujący, aby upewnić się, że możesz powiedzieć, że element był taki sam, jak element zwracany przez Last().

Takie podejście należy stosować ostrożnie, ponieważ Lastmoże być konieczne powtarzanie kolekcji. Chociaż może to nie stanowić problemu w przypadku małych kolekcji, jeśli się powiększy, może to mieć wpływ na wydajność. Nie powiedzie się również, jeśli lista zawiera zduplikowane elementy. W takich przypadkach coś takiego może być bardziej odpowiednie:

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
Potrzebowałem: kiedy pętla przechodzi przez ostatni element: foreach (wynik elementu w Model.Results) {if (result == Model.Results.Last ()) {<div> last </div>; } Wydaje się, że miałeś na myśli to samo.
nieszczęście

10
Kod będzie iterował dwa razy przez całą kolekcję - źle, jeśli kolekcja nie jest mała. Zobacz odpowiedź.
Shimmy Weitzhandler

54
To naprawdę nie działa, jeśli masz duplikaty w swojej kolekcji. Na przykład, jeśli pracujesz z kolekcją ciągów i istnieją jakieś duplikaty, to ten kod „różni się od ostatniego elementu” zostanie wykonany dla każdego wystąpienia ostatniego elementu na liście.
muttley91

7
Ta odpowiedź jest stara, ale dla innych, którzy patrzą na tę odpowiedź, możesz uzyskać ostatni element i upewnić się, że nie musisz zapętlać elementów, używając: Element last = Model.Results [Model.Results.Count - 1] Liczba właściwość listy nie wymaga zapętlania. Jeśli masz duplikaty na liście, po prostu użyj zmiennej iteratora w pętli for. Zwykłe stare dla pętli nie są złe.
Michael Harris,

Proponuję używać var last = Model.Result[Model.Result.Count - 1];szybciej niż za pomocąLast()
Tân

184

Co powiesz na dobrą, staroświecką pętlę?

for (int i = 0; i < Model.Results.Count; i++) {

     if (i == Model.Results.Count - 1) {
           // this is the last item
     }
}

Lub używając Linq i foreach:

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

14
Tak wielu ppl zastanawia się nad takim prostym problemem, kiedy pętla for jest już w stanie to zrobić. : \
Andrew Hoffman

Rozwiązanie Linq jest moim absolutnym faworytem! Dzięki za udostępnienie
mekograf

To jest bardziej odpowiednia odpowiedź niż zaakceptowana.
Ratul

Uwaga dla każdego, kto chce użyć rozwiązania LINQ w kolekcji ciągów (lub typów wartości): Zasadniczo nie zadziała, ponieważ porównanie == zakończy się niepowodzeniem, jeśli ostatni ciąg na liście pojawi się również wcześniej na liście. Działa to tylko wtedy, gdy pracujesz z listą, która nie ma zduplikowanych ciągów.
Tawab Wakil

Niestety nie możesz użyć tego sprytnego rozwiązania, jeśli Model.Resultsjest IEnumerable. Możesz wywoływać Count()przed pętlą, ale może to spowodować pełną iterację sekwencji.
Luca Cremonesi

42

Używanie Last()na niektórych typach zapętli całą kolekcję!
Oznacza to, że jeśli wykonasz a foreachi zadzwonisz Last(), zapętlisz dwa razy!czego jestem pewien, że chciałbyś uniknąć w dużych kolekcjach.

Następnie rozwiązaniem jest użycie do whilepętli:

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

Więc chyba rodzaj kolekcja jest typu funkcja iteracyjne thru wszystkich elementów kolekcji.IList<T>Last()

Test

Jeśli Twoja kolekcja zapewnia dostęp losowy (np. Narzędzia IList<T>), możesz również sprawdzić swój przedmiot w następujący sposób.

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

1
Czy na pewno moduł wyliczający potrzebuje usinginstrukcji? Pomyślałem, że jest to potrzebne tylko wtedy, gdy obiekt obsługuje zasoby systemu operacyjnego, ale nie w przypadku zarządzanych struktur danych.
Przyczajony kotek

IEnumerator nie implementuje IDisposable, więc linia z użyciem z podnosi błąd czasu kompilacji! +1 za rozwiązanie, przez większość czasu nie możemy po prostu użyć for zamiast zamiast foreach, ponieważ wyliczalne elementy kolekcji obliczają się w czasie wykonywania lub sekwencja nie obsługuje losowego dostępu.
Saleh


40

Jak pokazuje Chris, Linq będzie działać; po prostu użyj Last (), aby uzyskać odwołanie do ostatniego w wyliczalnym, i dopóki nie pracujesz z tym odwołaniem, zrób normalny kod, ale jeśli pracujesz z tym odwołaniem, zrób dodatkową rzecz. Jego wadą jest to, że zawsze będzie to złożoność O (N).

Zamiast tego możesz użyć funkcji Count () (która jest O (1), jeśli IEnumerable jest również ICollection; jest to prawdą w przypadku większości typowych wbudowanych IEnumerables), i możesz połączyć swój foreach z licznikiem:

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

  }
}

Cześć, to najlepsze jak dotąd podejście! Prosto i do rzeczy. Jedno podejście do programowania. Dlaczego nie wybieramy i dajemy temu +1 więcej i więcej!
Hanny Setiawan

1
Ostatni element powinien zostać znaleziony tylko raz ( Promuj zapamiętywanie ) przed foreachblokiem. Tak: var lastItem = objList.LastOrDeafault();. Następnie od wewnątrz foreachpętli można to sprawdzić w następujący sposób: f (item.Equals(lastItem)) { ... }. W twojej oryginalnej odpowiedzi objList.LastOrDefault()iterowałaby kolekcja po każdej iteracji „foreach” ( dotyczy to złożoności wielomianowej ).
AlexMelw

Zła odpowiedź. złożoność n ^ 2 zamiast n.
Shimmy Weitzhandler

11

Jak wskazał Shimmy, użycie Last () może stanowić problem z wydajnością, na przykład, jeśli twoja kolekcja jest żywym wynikiem wyrażenia LINQ. Aby zapobiec wielokrotnym iteracjom, możesz użyć metody rozszerzenia „ForEach” w następujący sposób:

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

Metoda rozszerzenia wygląda następująco (jako dodatkowy bonus, powie ci również indeks i jeśli patrzysz na pierwszy 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

Ulepszając odpowiedź Daniela Wolfa, możesz jeszcze postawić na innym, IEnumerableaby uniknąć wielu iteracji i lambd, takich jak:

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

Implementacja metody rozszerzenia:

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
Druga odpowiedź nie powtarza wielokrotnie źródła, więc to nie jest problem, który rozwiązujesz. Rzeczywiście zezwoliłeś na użycie foreach, co jest ulepszeniem.
Servy

1
@Servy Mam na myśli to. Oprócz pojedynczej iteracji od oryginalnej odpowiedzi unikam lambdas.
Fabricio Godoy

7

Implementacja iteratora tego nie zapewnia. Twoja kolekcja może być IListdostępna za pośrednictwem indeksu w O (1). W takim przypadku możesz użyć normalnej forpętli:

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

Jeśli znasz liczbę, ale nie możesz uzyskać dostępu za pomocą indeksów (dlatego wynikiem jest an ICollection), możesz policzyć siebie, zwiększając wartość iwforeach ciele i porównując ją z długością.

Wszystko to nie jest idealnie eleganckie. Rozwiązanie Chrisa może być najładniejsze, jakie do tej pory widziałem.


Porównując wydajność twojego licznika w ramach idei Foreach w porównaniu z rozwiązaniem Chrisa, zastanawiam się, co by kosztowało więcej - pojedyncze wywołanie Last () lub suma wszystkich dodanych operacji przyrostowych. Podejrzewam, że będzie blisko.
TTT

6

Co powiesz na trochę prostsze podejście.

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
Nie wiem, dlaczego ktoś cię zagłosował. Jest to całkowicie do przyjęcia, biorąc pod uwagę, że już wykonujesz foreach i ponosisz koszty o (n).
arviman

2
Pomimo tego, że odpowiedź jest idealna do znalezienia ostatniego elementu, nie odpowiada warunkowi OP..., określ, która jest ostatnią iteracją pętli ”. Tak więc, nie jesteś w stanie ustalić, że ostatnia iteracja jest w rzeczywistości ostatnią, a zatem nie mogłeś potraktować jej inaczej ani nawet zignorować. To jest powód, dla którego ktoś cię ocenił. @arviman, byłeś tak ciekawy.
AlexMelw

1
Masz rację, całkowicie mi tego brakowało @ Andrey-WD. Myślę, że rozwiązaniem tego problemu jest wywołanie „last” jeden raz przed pętlą (nie można tego zrobić wewnątrz pętli, jak byłoby O (N ^ 2), a następnie sprawdzić, czy odniesienie do niej pasuje.
arviman

5

Najlepszym rozwiązaniem byłoby prawdopodobnie wykonanie tego kroku po pętli: np

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

//Post execution logic

Lub jeśli musisz zrobić coś z ostatnim rezultatem

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

Item lastItem = Model.Results[Model.Results.Count - 1];

//Execute logic on lastItem here

3

Akceptowana odpowiedź nie będzie działać dla duplikatów w kolekcji. Jeśli ustawiono opcję foreach, możesz po prostu dodać własne zmienne indeksujące.

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 ()” nie działało dla mnie, więc musiałem zrobić coś takiego:

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

Wprowadzając drobne poprawki w doskonałym kodzie Jona Skeeta, możesz nawet uczynić go bardziej inteligentnym, umożliwiając dostęp do poprzedniego i następnego elementu. Oczywiście oznacza to, że będziesz musiał przeczytać z wyprzedzeniem 1 element w implementacji. Ze względu na wydajność poprzedni i następny element są zachowywane tylko dla bieżącego elementu iteracji. To wygląda tak:

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

Jak przekonwertować, foreachaby zareagować na ostatni element:

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

Wystarczy zapisać poprzednią wartość i pracować z nią w pętli. Następnie na końcu „poprzednia” wartość będzie ostatnim elementem, co pozwoli ci postępować inaczej. Nie wymaga liczenia ani specjalnych bibliotek.

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

Możesz po prostu użyć pętli for i nie ma potrzeby dodawania dodatkowych elementów ifwewnątrz forciała:

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

-1W forstanie dba o pomijam ostatni element.


-1 w pętli for nie zajmuje się pomijaniem ostatniego elementu. Otrzymałbyś wyjątek IndexOutOfRangeException, gdybyś nie uwzględnił -1.
Jaa H


0

Aby zrobić coś dodatkowego dla każdego elementu oprócz ostatniego, można zastosować podejście oparte na funkcjach.

delegate void DInner ();

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

Podejście to ma oczywiste wady: mniej klarowności kodu w bardziej złożonych przypadkach. Dzwonienie do delegatów może nie być bardzo skuteczne. Rozwiązywanie problemów może nie być łatwe. Jasna strona - kodowanie jest fajne!

Powiedziawszy to, sugerowałbym użycie zwykłego dla pętli w trywialnych przypadkach, jeśli wiesz, że liczba twoich kolekcji nie jest strasznie wolna.


0

Innym sposobem, którego nie widziałem, jest użycie kolejki. Jest to analogiczne do sposobu implementacji metody SkipLast () bez iteracji więcej niż to konieczne. W ten sposób możesz to zrobić na dowolnej liczbie ostatnich elementów.

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

Aby to nazwać, wykonaj następujące czynności:

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

Możesz utworzyć metodę rozszerzenia specjalnie dedykowaną do tego:

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

i możesz użyć tego w następujący sposób:

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

0

Na podstawie odpowiedzi @ Shimmy stworzyłem metodę rozszerzenia, która jest rozwiązaniem, którego wszyscy chcą. Jest prosty, łatwy w użyciu i tylko raz zapętla kolekcję.

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

Działa to na każdym IEnumerable<T>. Sposób użycia wygląda następująco:

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

Wyjście wygląda następująco:

1,
2,
3,
4,
5

Dodatkowo możesz zrobić z tego Selectmetodę stylu. Następnie ponownie użyj tego rozszerzenia w ForEach. Ten kod wygląda następująco:

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

-1

Możemy sprawdzić ostatni element w pętli.

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

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

(!) Bez względu na to, jak dobry lub zły jest twój kod. Bez wyjaśnienia zwykle nie ma żadnej wartości.
AlexMelw

Wydaje się również, że jest to zbyt skomplikowane.
mekograf

-3

Możesz to zrobić w następujący sposób:

foreach (DataGridViewRow dgr in product_list.Rows)
{
    if (dgr.Index == dgr.DataGridView.RowCount - 1)
    {
        //do something
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.