Resharper-Beispielcode zur Erläuterung von "Mögliche mehrfache Aufzählung von IEnumerable"

76

Manchmal warnt Resharper vor:

Mögliche mehrfache Aufzählung von IEnumerable

Es gibt eine SO-Frage zum Umgang mit diesem Problem , und die ReSharper-Site erklärt hier auch die Dinge . Es enthält einen Beispielcode, der Sie dazu auffordert:

IEnumerable<string> names = GetNames().ToList();

Meine Frage bezieht sich auf diesen speziellen Vorschlag: Wird dies nicht immer noch dazu führen, dass die Sammlung in den 2 for-each-Schleifen zweimal aufgelistet wird?

user2250250
quelle

Antworten:

183

GetNames()gibt ein zurück IEnumerable. Wenn Sie dieses Ergebnis speichern:

IEnumerable foo = GetNames();

Jedes Mal foo, wenn Sie aufzählen , wird die GetNames()Methode erneut aufgerufen (nicht wörtlich, ich kann keinen Link finden, der die Details richtig erklärt, aber siehe IEnumerable.GetEnumerator()).

Resharper sieht dies und schlägt vor, das Ergebnis der Aufzählung GetNames()in einer lokalen Variablen zu speichern , indem Sie es beispielsweise in einer Liste materialisieren:

IEnumerable fooEnumerated = GetNames().ToList();

Dadurch wird sichergestellt, dass das GetNames()Ergebnis nur einmal aufgelistet wird, solange Sie sich darauf beziehen fooEnumerated.

Dies ist wichtig, da Sie normalerweise nur einmal auflisten möchten, z. B. wenn Sie GetNames()einen (langsamen) Datenbankaufruf ausführen.

Da Sie die Ergebnisse in einer Liste materialisiert haben , spielt es keine Rolle mehr, dass Sie fooEnumeratedzweimal aufzählen . Sie werden zweimal eine In-Memory-Liste durchlaufen.

CodeCaster
quelle
Ah! Das erklärt es.
user2250250
1
Nein. Die GetEnumerator () -Methode wird in der foreach-Schleife nur einmal aufgerufen. Der wahre Grund ist das Risiko schmutziger Daten. In GetNames () gibt es beispielsweise eine SQL-Abfrage, jedoch nur eine Abfrage, die IEnurable zurückgibt. Wenn Sie .ToList () aufrufen und alle Daten im Speicher speichern, besteht nur ein geringes Risiko für fehlerhafte Daten. Wenn jedoch zwischen zwei Schleifenoperationen viel Zeit liegt und Sie SQL jedes Mal in die Datenbank ausführen, besteht ein großes Risiko für verschmutzte Daten.
Sun Robin
6
Dies ist die URL von Microsoft, die uns die interne Implementierung der foreach-Schleife mitteilt. Sie können sehen, dass der GetEnumerator () nur einmal aufgerufen wird. Und eine andere Sache, die wir wissen müssen, ist, dass IEnumerable beim Laden mit einem ORM verzögert geladen wird. msdn.microsoft.com/en-us/library/aa664754(v=vs.71).aspx Wenn Sie nur den Handler von Enumerator erhalten und nicht alle Daten in den Speicher laden, kann es zu Konflikten zwischen den beiden Vorgängen mit dem kommen Im selben Enumerator hat jemand die Datenbank geändert. Um dies zu vermeiden, empfiehlt Resharper, die Daten über die Methode .ToList () in Merroy zu laden.
Sun Robin
5
Diese Antwort ist etwas ungenau. Nicht jeder IEnumerablewird für jede Aufzählung ausgewertet, nur diejenigen, die mit 'verzögerter Ausführung' implementiert sind (das ist der Link, den Sie vermisst haben, @CodeCaster). Die R # -Warnung kann ignoriert werden, wenn die zugrunde liegende Implementierung nicht verzögert ausgeführt wird, wie in diesem Fragenbeispiel, das aufruft ToList(), um sie in einer anderen IEnumerableVariablen zu speichern .
Frédéric
2
@CodeCaster, kein Problem, ich kam von einer kürzlich verlinkten Antwort und fügte eine kurze Erklärung hinzu, was imo fehlte. Fühlen Sie sich frei, es wiederzuverwenden, wenn Sie möchten. Dann werde ich wahrscheinlich meine Kommentare hier löschen, sobald sie nicht mehr relevant sind.
Frédéric
11

Ich fand, dass dies der beste und einfachste Weg ist, mehrere Aufzählungen zu verstehen.

C # LINQ: Mögliche mehrfache Aufzählung von IEnumerable

https://helloacm.com/c-linq-possible-multiple-enumeration-of-ienumerable-resharper/

Ryan
quelle
Sie könnten hier auf besondere Punkte Ihres Links hinweisen .. aber der Link ist nett -> +1
LuckyLikey
Dies ist eine großartige und einfache Erklärung mit einem Beispiel!
dekompiliert
9

GetNames()wird nicht zweimal aufgerufen. Die Implementierung von IEnumerable.GetEnumerator()wird jedes Mal aufgerufen, wenn Sie die Sammlung mit auflisten möchten foreach. Wenn innerhalb der IEnumerable.GetEnumerator()einige teure Berechnung gemacht wird, könnte dies ein Grund sein, darüber nachzudenken.

IronMan702
quelle
5

Ja, Sie werden es ohne Zweifel zweimal aufzählen. Der Punkt ist jedoch, dass, wenn GetNames()eine verzögerte Linq-Abfrage zurückgegeben wird, deren Berechnung sehr teuer ist, diese zweimal ohne Aufruf von ToList()oder berechnet wird ToArray().

Sriram Sakthivel
quelle