foreach mit Index (Laufvariable)
-
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.
-
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.
-
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. DasToList
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üssliIch 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. DasToList
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
-
µ schrieb:
EDIT: Jetzt komme ich auch ins Schleudern. Wurde Count() für z.B. List überschrieben?
Geht das überhaupt? Wird für Extension Methods ein Dispatch gemacht?
µ schrieb:
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.
Ich habs glaub ich schonmal gesagt: Die eigentliche Warze ist foreach. Ein Statement, das genau dann elegante Lösungen ermöglicht, wenn man genau das machen will, was sich der Designer gedacht hat. Kleine Abweichungen: Pech gehabt!
(Vielleicht ist es auch meine Lisp-Vergangenheit. Lernt bloß nie Lisp, dann ist alles frustrierend.)
-
Es gibt eine Sprache die innerhalb von foreach automatisch eine Variable namens $ zur Verfügung stellt die als Laufvariable dient. Das ist wunderschön *mir für C# wünsch* *auch wenn mich Bashar knüppelt*
MfG SideWinder
-
Bashar schrieb:
µ schrieb:
EDIT: Jetzt komme ich auch ins Schleudern. Wurde Count() für z.B. List überschrieben?
Geht das überhaupt? Wird für Extension Methods ein Dispatch gemacht?
Sieht nicht so aus, aber es gibt offenbar Wege: http://www.codeproject.com/KB/tips/PolymorphExtensionVisitor.aspx
*volkards Posting nicht ganz versteh, mir jemand erklären will?*
MfG SideWinder
-
Noob3453 schrieb:
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
Nicht nur wegen der Abbruchbedingung. Es können auch Nebeneffekte erst in der Schleife auftreten, welche das Ergebnis verändern würden. Wie gesagt, einfach mit Vorsicht behandeln.
@μ,
Empfinde ich seltsam, dass du immer wieder einen Index brauchst. Deshalb habe ich nämlich nachgefragt. Bei der Antwort von Bashar auf meine Frage, sieht man, dass er es eher in Ausnahmefällen benötigt. Bei dir scheint das ja eher ganz normal zu sein.
Ich frage mich daher, ob du nicht irgendetwas falsch machst? Vielleicht solltest du deine Design etwas überdenken? Wieso genau brauchst du immer wieder einen Index? Könnte man es anders lösen? Den Index als ID zu nehmen, halte ich übrigens für extrem gefährlich. Sowas sollte man nur temporär machen. Aber wieso überhaupt? Du hast ja sowieso nur einIEnumerable
, wieso brauchst du dann eine ID? Was wäre die Alternative zu dieser ID? usw.@all,
LinqCount
verwendetICollection<T>.Count
, sofern dies zur Verfügung steht:
http://msdn.microsoft.com/en-us/library/bb338038.aspxMSDN schrieb:
Remarks
If the type of source implementsICollection<T>
, that implementation is used to obtain the count of elements. Otherwise, this method determines the count.Grüssli