Zuweisung von Speicher bei der Verwendung von foreach-Schleifen in C#
Ich kenne die Grundlagen, wie foreach-Schleifen arbeiten in C# (Wie foreach-Schleifen arbeiten, die in C#)
Frage ich mich, ob die Verwendung der foreach-Speicher zuweist, verursachen Müll-Sammlungen? (für alle, eingebaute System-Typen).
Beispielsweise mit dem Reflektor auf der System.Collections.Generic.List<T>
Klasse, hier ist die Implementierung von GetEnumerator:
public Enumerator<T> GetEnumerator()
{
return new Enumerator<T>((List<T>) this);
}
Auf jede Nutzung dieser ordnet einen neuen Enumerator (und noch mehr Müll).
Alle Arten, dies zu tun? Wenn ja, warum? (kann nicht eine einzelne Enumerator wiederverwendet werden?)
a) Dies ist eine Implementierung detail. Diese sind irrelevant. b) Der GC existiert.
Ich denke, das Hauptproblem mit single-Enumerator wäre thread-Sicherheit.
Dies ist nicht beschränkt auf
Jeder, der kommt an dieser Seite ist wohl auch hier für einen guten Grund. Es gibt definitiv Zeiten, wenn jede einzelne Zuordnung ist wichtig und sollte möglichst vermieden werden. ("Don' T worry about it", mag wahr sein, 99% der Zeit, aber wenn es darauf ankommt, kann es die Materie eine ganze Menge.)
Die anti-perf Denkweise von vielen Rednern meiner Lieblingssprache C#, und die ständige stach von Ihnen, eine sehr große Gruppe, bei der bloßen Andeutung, dass jemand das sorgen über eine low-level-perf Anliegen (siehe Erster Kommentar oben), haben in der Vergangenheit schon schädlich genug, um zu überlegen, ob dies die richtige Sprache für diejenigen von uns, die tun Pflege. Solche Mentalitäten beigetragen zu einem Jahrzehnt und halten von C# / .NET von der Fokussierung auf lean and mean. Ich bin so froh, dass Mentalität wurde nun ersetzt mit netcore, ich wünschte nur, es würde durch die Reihen schneller.
Ich denke, das Hauptproblem mit single-Enumerator wäre thread-Sicherheit.
Dies ist nicht beschränkt auf
foreach()
. Die ganze Idee des habens eines GC ist, dass kleine, kurzlebige Objekte sind sehr Billig. Wir "verschwenden" Sie die ganze Zeit, wie in text = text.ToLower();
. Mach dir keine sorgen. Die meisten re-use-Regelungen erweisen teurer.Jeder, der kommt an dieser Seite ist wohl auch hier für einen guten Grund. Es gibt definitiv Zeiten, wenn jede einzelne Zuordnung ist wichtig und sollte möglichst vermieden werden. ("Don' T worry about it", mag wahr sein, 99% der Zeit, aber wenn es darauf ankommt, kann es die Materie eine ganze Menge.)
Die anti-perf Denkweise von vielen Rednern meiner Lieblingssprache C#, und die ständige stach von Ihnen, eine sehr große Gruppe, bei der bloßen Andeutung, dass jemand das sorgen über eine low-level-perf Anliegen (siehe Erster Kommentar oben), haben in der Vergangenheit schon schädlich genug, um zu überlegen, ob dies die richtige Sprache für diejenigen von uns, die tun Pflege. Solche Mentalitäten beigetragen zu einem Jahrzehnt und halten von C# / .NET von der Fokussierung auf lean and mean. Ich bin so froh, dass Mentalität wurde nun ersetzt mit netcore, ich wünschte nur, es würde durch die Reihen schneller.
InformationsquelleAutor lysergic-acid | 2013-08-31
Du musst angemeldet sein, um einen Kommentar abzugeben.
Foreach verursachen können, vornehmen, aber zumindest in neueren Versionen .NET und Mono ist, spielt es nicht, wenn man sich mit den konkreten
System.Collections.Generic
Typen oder arrays. Ältere Versionen dieser Compiler (wie die version von Mono verwendet, die von Unity3D bis 5.5) immer generieren Zuweisungen.Den C# - compiler verwendet duck typing suchen
GetEnumerator()
- Methode und verwendet, wenn möglich. Die meistenGetEnumerator()
Methoden aufSystem.Collection.Generic
Typen haben GetEnumerator () - Methoden, die Rückkehr structs und arrays werden speziell behandelt. Wenn IhrGetEnumerator()
Methode nicht zuordnen, Sie können in der Regel vermeiden Zuweisungen.Jedoch, erhalten Sie immer eine Zuteilung, wenn Sie es mit einer der Schnittstellen
IEnumerable
,IEnumerable<T>
,IList
oderIList<T>
. Auch wenn Ihre Umsetzung-Klasse gibt ein struct, struct wird boxed und cast zuIEnumerator
oderIEnumerator<T>
, das erfordert eine Zuteilung.HINWEIS: Da die Einheit 5.5 aktualisiert, um C# 6, ich kenne keine aktuelle compiler-release, der noch dieses zweiten Zuweisung.
Gibt es einen zweiten Zuweisung, die ist ein wenig komplizierter zu verstehen. Nehmen Sie diese foreach-Schleife:
Bis C# 5.0, es wird erweitert, um so etwas wie dies (mit gewissen kleinen Unterschiede):
Während
List<int>.Enumerator
ist eine Struktur, und es muss nicht auf dem heap zugeordnet werden, ist die Besetzungenumerator as System.IDisposable
Felder der struct, die eine Zuweisung. Die Skillung geändert mit C# 5.0, Verbot der Zuweisung, aber .NET brach der spec und optimiert die Zuweisung entfernt früher.Diese Verteilungen sind extrem gering. Beachten Sie, dass eine Zuweisung ist sehr Verschieden von einem memory leak, und mit der garbage-collection, die Sie in der Regel nicht haben, sich darum zu kümmern. Es gibt jedoch einige Szenarien, in denen Sie interessieren, auch diese Zuordnungen. Ich mache Unity3D arbeiten und bis 5.5, wir konnten nicht alle Zuordnungen, die in Vorgänge, die passieren jedem Spiel frame, denn wenn der garbage collector ausgeführt wird, erhalten Sie einen spürbaren Stich.
Beachten Sie, dass die foreach-Schleifen über arrays werden speziell behandelt und haben nicht nennen Entsorgen. So weit ich es sagen kann, foreach hat nie zugewiesen, wenn Schleifen über arrays.
Warum Sie sagte, dass TestHash nicht zuordnen, ob in IL-Auflistung können Sie deutlich sehen, das in derselben Besetzung zu IDisposable-wie in TestIList? Oder meinst du, dass es keine Zuweisung vor der Schleife, aber immer noch gibt es eine nach? Könnten Sie das bitte klären?
Vielleicht ist das ildasm verwirrt das Problem. Als ich das erste beantwortet die Frage, ich wusste nicht, die ganze Geschichte über die IDisposable-Zuweisungen. Die ildasm zeigt nur, modern Visual Studio, die nicht mehr reserviert, die von casting zu
IDisposable
. Wenn Sie dort waren, würden Sie sehen, eine box, Anleitung. Statt der Zuteilung auf der IList wird begraben in derGetEnumerator
nennen, weil es um die box seinen Rückgabewert zuIEnumerator<T>
.InformationsquelleAutor hangar
Nein, das aufzählen einer Liste führt nicht dazu, dass garbage collections.
Den enumerator für die
List<T>
Klasse keinen Speicher auf dem heap. Es ist eine Struktur, nicht eine Klasse, so dass der Konstruktor nicht alloziert ein Objekt nur einen Wert zurückgibt. Dieforeach
- code würde verhindern, dass Wert auf dem stack, nicht auf dem heap.Enumeratoren für andere Sammlungen können Klassen aber, was würde der Zuweisung eines Objekts auf dem heap. Sie müssten zu prüfen, die Art der enumerator für jeden Fall sicher zu sein.
InformationsquelleAutor Guffa
Weil ein enumerator hält das aktuelle Element. Es ist wie ein cursor mit Datenbanken abgeglichen. Wenn mehrere threads würden auf den gleichen enumerator, würden Sie verlieren die Kontrolle über die Reihenfolge. Und Sie müssten, um es zurückzusetzen, um das erste Element jedes mal, wenn ein foreach berät.
InformationsquelleAutor J. van Langen
Wie bereits erwähnt in den Kommentaren, diese sollten in der Regel nicht ein Problem, müssen Sie kümmern, wie das ist der Punkt der Garbage Collection. Das heißt, mein Verständnis ist, dass ja, jeder foreach-Schleife erzeugt einen neuen Enumerator-Objekt, die schließlich von der garbage Collection freigegeben. Um zu sehen, warum, schauen Sie in die Dokumentation für die Schnittstelle hier. Wie Sie sehen können, gibt es eine Funktion nennen, welche Anträge für das nächste Objekt. Die Fähigkeit, dies zu tun bedeutet, die Enumerators Staat, und müssen wissen, welches die nächste ist. Wie, warum dies ist notwendig, Bild, das Sie verursacht eine Interaktion zwischen jede permutation der Sammlung Elemente:
Hier haben Sie zwei Zähler auf die gleiche Kollektion zur gleichen Zeit. Klar einem freigegebenen Speicherort würde nicht Ihr Ziel erreichen.
InformationsquelleAutor aring