foreach mit Index (Laufvariable)



  • Rhombicosidodecahedron schrieb:

    Tolle Idee. 🙂

    Danke 🙂

    Ich lese eure Vorschläge interessiert.
    Aber Linq-Konstrukte wie hier vorgestellt sind zu viel Schreibarbeit. Die einfachste, natürlichste Erweiterung für foreach ist gesucht.

    Firefighter schrieb:

    Ich sag mal so: Wenn du nen Index brauchst, dann ist die for-Schleife in meinen Augen die perfekte Wahl.

    Wenn aber wirklich nur ein IEnumerable und keine List o.Ä. zur Verfügung steht, ist eine for-Schleife inakzeptabel.



  • Rhombicosidodecahedron schrieb:

    Die üblichen SecurityChecks fehlen aber auch noch.
    ]

    Welchen sinnvollen Fehlerfall, der sich nicht schon mit einer bekannten Exception bemerkbar macht, wäre denn noch abzufangen?



  • Rhombicosidodecahedron schrieb:

    Die Select-Methode ruft die Transformationsfunktion, der zweite Parameter, schon mit dem jeweiligen Index auf.

    Danke 🙂 Aber typisch MS mal wieder ... 🙂



  • Firefighter schrieb:

    Ich halte dahingegen deins etwas Oversized, zumal du ja jetzt überall wo du deine IndexedEnumerable benutzen willst den Quellcode mit ausliefern oder einbinden musst. Ist es dir das wirklich Wert wegen einer anderen Schleife?

    Das ist eigentlich ein interessanter Punkt. Hältst Du es für fragwürdig solchen Code in jedes Projekt, das IndexedEnumerable verwendet, einzubinden? Warum? Und Oversized: Stimmt schon. Aber schau Dir mal den LINQ-Overhead im Allgemeinen an. Für performancekritischen Code würde ich sowas auch nicht verwenden.

    Jedes größere Projekt hat Tool-, Helper-Klassen oder (in C#) Extension-Methoden. Ich habe z.B. eine Reihe Extensions für string und es tat mir bisher nicht weh, diese in alle Projekte einzubinden.



  • Und jetzt nochmal in "kurz". Danke für die Inspiration 🙂

    using System;
    using System.Linq;
    using System.Collections.Generic;
    
    namespace IndexForeach
    {
        class Program
        {
            static void Main(string[] args)
            {
                var myList = new List<string>(new[] { "Hallo", "wie", "gehts", "denn", "so" });
    
                Console.WriteLine("---> Standardindizierung");
    
                foreach (var pair in myList.ToIndexedEnumerable())
                    Console.WriteLine("index = {0}, value = {1}", pair.Key, pair.Value);
    
                Console.WriteLine("\n---> Ungerade Zahlen: Startindex = 1, Step = 2");
    
                foreach (var pair in myList.ToIndexedEnumerable(1, 2))
                    Console.WriteLine("index = {0}, value = {1}", pair.Key, pair.Value);
    
                Console.WriteLine("\n---> Quadratzahlen als Index");
    
                foreach (var pair in myList.ToIndexedEnumerable(1, i => i * i))
                    Console.WriteLine("index = {0}, value = {1}", pair.Key, pair.Value);
    
                Console.ReadKey(true);
            }
        }
    
        static class Extensions
        {
            public static IEnumerable<KeyValuePair<int, T>> ToIndexedEnumerable<T>(this IEnumerable<T> collection)
            {
                return collection.ToIndexedEnumerable(0, 1, i => i);
            }
    
            public static IEnumerable<KeyValuePair<int, T>> ToIndexedEnumerable<T>(this IEnumerable<T> collection, int start, int step)
            {
                return collection.ToIndexedEnumerable(start, step, i => i);
            }
    
            public static IEnumerable<KeyValuePair<int, T>> ToIndexedEnumerable<T>(this IEnumerable<T> collection, Func<int, int> indexMapper)
            {
                return collection.ToIndexedEnumerable(0, 1, indexMapper);
            }
    
            public static IEnumerable<KeyValuePair<int, T>> ToIndexedEnumerable<T>(this IEnumerable<T> collection, int start, Func<int, int> indexMapper)
            {
                return collection.ToIndexedEnumerable(start, 1, indexMapper);
            }
    
            public static IEnumerable<KeyValuePair<int, T>> ToIndexedEnumerable<T>(this IEnumerable<T> collection, int start, int step, Func<int, int> indexMapper)
            {
                return collection.Select((item, index) => new KeyValuePair<int, T>(indexMapper(start + index * step), item));
            }
        }
    }
    

    ---> Standardindizierung
    index = 0, value = Hallo
    index = 1, value = wie
    index = 2, value = gehts
    index = 3, value = denn
    index = 4, value = so

    ---> Ungerade Zahlen: Startindex = 1, Step = 2
    index = 1, value = Hallo
    index = 3, value = wie
    index = 5, value = gehts
    index = 7, value = denn
    index = 9, value = so

    ---> Quadratzahlen als Index
    index = 1, value = Hallo
    index = 4, value = wie
    index = 9, value = gehts
    index = 16, value = denn
    index = 25, value = so



  • µ schrieb:

    Firefighter schrieb:

    Ich halte dahingegen deins etwas Oversized, zumal du ja jetzt überall wo du deine IndexedEnumerable benutzen willst den Quellcode mit ausliefern oder einbinden musst. Ist es dir das wirklich Wert wegen einer anderen Schleife?

    Das ist eigentlich ein interessanter Punkt. Hältst Du es für fragwürdig solchen Code in jedes Projekt, das IndexedEnumerable verwendet, einzubinden? Warum? Und Oversized: Stimmt schon. Aber schau Dir mal den LINQ-Overhead im Allgemeinen an. Für performancekritischen Code würde ich sowas auch nicht verwenden.

    Jedes größere Projekt hat Tool-, Helper-Klassen oder (in C#) Extension-Methoden. Ich habe z.B. eine Reihe Extensions für string und es tat mir bisher nicht weh, diese in alle Projekte einzubinden.

    Dieses Argument befördert dich auf das Siegertreppchen. Du hast mich überzeugt. Eigentlich hast du Recht das es nicht so dramatisch ist das einfach mit einzufügen.


  • Administrator

    Ich hätte da mal eine andere Frage: Wozu brauchst du diesen Index regelmässig? Kannst du da Beispiele nennen?

    Grüssli



  • Dravere schrieb:

    Ich hätte da mal eine andere Frage: Wozu brauchst du diesen Index regelmässig? Kannst du da Beispiele nennen?

    Ich brauchte das, um Sequenzen von einer API in eine (nicht ganz so tolle) andere API umzuschaufeln, oder irgendwelche IDs dazu zu generieren, oder einfach um sie auszugeben, sowas in der Art. Klar, dass man das normalerweise nicht brauchen sollte, aber es kommt halt immer mal wieder vor.



  • Was spricht denn gegen eine for-Schleife und einer Liste, wenn du den index benötigst? Ich als Anfänger hätte das einfach folgendermaßen gelöst.

    List<string> myList = getEnumerableFromHell().ToList();
    


  • Noob3453 schrieb:

    Was spricht denn gegen [...]

    Dass man die Sequenz dazu erstmal kopieren muss. Außerdem: Der Stolz 🙂



  • Lesbarkeit geht vor.
    Ich habe die hier aufgezeigten Möglichkeiten gesehen und halte sie auch stilvoll usw, aber ich würde lieber zu einer for greifen, ist zum einen bekannter (lernt schon jeder Anfänger) und fördert es eher die Lesbarkeit.

    Im übrigen, "Sequenz kopieren"? Wenn du eine IEnumerable hast daraus eine Liste machst werden auch nur die Referenzen geholt und gesammelt in einer Liste angeboten. Die Leistung dürfte zu vernachlässigen sein, außer eventuell das alle Referenzen direkt aufgelöst werden.


  • Administrator

    David W schrieb:

    Lesbarkeit geht vor.
    Ich habe die hier aufgezeigten Möglichkeiten gesehen und halte sie auch stilvoll usw, aber ich würde lieber zu einer for greifen, ist zum einen bekannter (lernt schon jeder Anfänger) und fördert es eher die Lesbarkeit.

    for(int i = 0; enumerator.MoveNext(); ++i)
    {
     // ...
    }
    

    Hat was 🙂

    David W schrieb:

    Im übrigen, "Sequenz kopieren"? Wenn du eine IEnumerable hast daraus eine Liste machst werden auch nur die Referenzen geholt und gesammelt in einer Liste angeboten. Die Leistung dürfte zu vernachlässigen sein, außer eventuell das alle Referenzen direkt aufgelöst werden.

    Mit solchen Aussagen wäre ich vorsichtig. Vor allem wenn es um allgemeine Aussagen geht. Man sollte nie vergessen, dass es auch Linq to SQL gibt. Ein IEnumerable könnte plötzlich Abfragen in einer Datenbank machen. Das ToList führt dann alle Abfragen durch, womöglich ungewollt.

    Grüssli



  • Dravere schrieb:

    Ich hätte da mal eine andere Frage: Wozu brauchst du diesen Index regelmässig? Kannst du da Beispiele nennen?
    Grüssli

    Ich komme immer wieder in die Situation, dass ich gegen die IEnumerable-Schnittstelle programmiere und mit foreach darüber iteriere. Im Laufe der Entwicklung sich aber zeigt, dass ein Index notwendig ist. Zum Beispiel für IDs oder andere Berechnungen. Bisher hab ich dann oft mit der Schnittstellenprogrammierung gebrochen und auf Lists oder andere konkrete, indizierbare Typen zurück gegriffen. Oder eine Laufvariable außerhalb des foreach-scope eingeführt. Alles nicht das Wahre.

    ToIndexedEnumerable() gefällt mir jetzt sehr gut (auch bei der Eingabe: ".ToI" <Tab>. Null Aufwand). Der umfangreiche Code hat mich anfangs noch gestört aber mit ein wenig Inspiration von diesem Thread hat sich das auf ein akzeptables Niveau reduziert. Ich denke mal am Montag fließt das in den Produktivcode ein 🙂

    Es gibt noch zwei Sonderfälle einer Iteration: Das erste und das letzte Element müssen häufig gesondert von den restlichen Elementen einer Enumeration behandelt werden. Das erste Element kann mit dem Code jetzt abgefangen werden. Der Index steht ja zur Verfügung und man kennt den Startindex. Für das letzte Element hab ich noch keine elegante und performante Lösung. Count() von LINQ ist inakzeptabel wegen dem linearen Aufwand.
    Mal schauen.... 🙂



  • Count() von LINQ ist inakzeptabel wegen dem linearen Aufwand.

    Huh? Hat es das auch wenn darunter eine Collection liegt die ihre größte in O(1) weiß? Meiner einer muss am Montag sein Projekt durchsuchen. Hab bestimmt Count() verwendet...

    MfG SideWinder



  • Wenn Count() gegen IEnumerable aufgerufen wird, hat es linearen Aufwand. Unabhängig von der konkreten Klasse dahinter.

    EDIT: Jetzt komme ich auch ins Schleudern. Wurde Count() für z.B. List überschrieben?



  • SideWinder schrieb:

    Meiner einer muss am Montag sein Projekt durchsuchen. Hab bestimmt Count() verwendet...

    Tja. Du bist halt eine Flasche, sagen manche Anfänger. Deine Verfehlungen haben bestimmt mindestens 0 Tage Arbeitszeit gekostet.
    Da bin ich sicher. Würdest Du in großen Projekten arbeiten, wäre es sogar fünfmal so viel.



  • *nehme mal Sarkasmus an, bin schon etwas müde*

    Ich sag ja nicht, dass ich da mit so großen Datenmengen arbeiten würde, als dass es einen Unterschied machen würde, aber es sieht halt dann nicht gut aus. Und ein kleines "Find in solution" tut nun auch nicht weh 😉

    MfG SideWinder



  • volkard schrieb:

    Tja. Du bist halt eine Flasche, sagen manche Anfänger.

    Hast Du irgendein persönliches Problem mit mir?



  • µ schrieb:

    volkard schrieb:

    Tja. Du bist halt eine Flasche, sagen manche Anfänger.

    Hast Du irgendein persönliches Problem mit mir?

    Nein, DU bist für mich ein Profi im nichteigenlichen (im eigentlichen vielleicht auch, das kann ich aber kaum wissen) Sinne. Du bist mir sehr oft mit einer Expertise positiv aufgefallen.



  • Dravere schrieb:

    David W schrieb:

    Im übrigen, "Sequenz kopieren"? Wenn du eine IEnumerable hast daraus eine Liste machst werden auch nur die Referenzen geholt und gesammelt in einer Liste angeboten. Die Leistung dürfte zu vernachlässigen sein, außer eventuell das alle Referenzen direkt aufgelöst werden.

    Mit solchen Aussagen wäre ich vorsichtig. Vor allem wenn es um allgemeine Aussagen geht. Man sollte nie vergessen, dass es auch Linq to SQL gibt. Ein IEnumerable könnte plötzlich Abfragen in einer Datenbank machen. Das ToList führt dann alle Abfragen durch, womöglich ungewollt.

    Grüssli

    Aber wenn ich eh mit foreach eine Iteration über die gesamte Menge laufen lasse, spielt das dann aber auch keine Rolle mehr. Außer das ein paar Referenzen kopierte werden, wie David oben angemerkt hat. Wenn man in seiner foreach Schleife eine Abbruchbedingung drinnen hat, dann ist mir schon klar, das ToList nicht immer elegant ist


Anmelden zum Antworten