Bedenken Sie:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
Befindet sich obj
ein Verweis auf das entsprechende Objekt in der Liste, sodass beim Ändern der Eigenschaft die Änderung in der Objektinstanz bestehen bleibt, sobald sie irgendwo erstellt wurde?
Antworten:
Ja,
obj
ist ein Verweis auf das aktuelle Objekt in der Sammlung (vorausgesetzt, esMyClass
handelt sich tatsächlich um eine Klasse). Wenn Sie Eigenschaften über die Referenz ändern, ändern Sie das Objekt wie erwartet.Beachten Sie jedoch, dass Sie die Variable
obj
selbst nicht ändern können, da es sich um die Iterationsvariable handelt. Wenn Sie es versuchen, wird ein Kompilierungsfehler angezeigt. Das bedeutet, dass Sie es nicht auf Null setzen können und wenn Sie Werttypen iterieren, können Sie keine Mitglieder ändern, da dies den Wert ändern würde.In der C # -Sprachenspezifikation heißt es (8.8.4)
quelle
Ja, bis Sie den generischen Typ von List in IEnumerable ändern.
quelle
Sie haben hier zwei verschiedene Fragen gestellt. Nehmen wir sie in die richtige Reihenfolge.
Iteriert eine foreach-Schleife als Referenz?
Wenn Sie im gleichen Sinne wie eine C ++ for-Schleife als Referenz meinen, dann nein. C # hat keine lokalen Variablenreferenzen im gleichen Sinne wie C ++ und unterstützt daher diese Art der Iteration nicht.
Wird die Änderung bestehen bleiben?
Angenommen, MyClass ist ein Referenztyp, lautet die Antwort Ja. Eine Klasse ist ein Referenztyp in .Net, und daher ist die Iterationsvariable eine Referenz auf die eine Variable, keine Kopie. Dies würde für einen Werttyp nicht zutreffen.
quelle
Nun, es ist mir passiert, dass meine Änderungen nicht in einer foreach-Schleife aktualisiert wurden, als ich die var-Sammlung durchlaufen habe:
var players = this.GetAllPlayers(); foreach (Player player in players) { player.Position = 1; }
Als ich var in List änderte, fing es an zu funktionieren.
quelle
Sie können dies in diesem Fall (mit a
List<T>
) tun, aber wenn Sie über das Generikum iterierenIEnumerable<T>
, wird es von seiner Implementierung abhängig.Wenn es immer noch ein
List<T>
oderT[]
wäre, würde alles wie erwartet funktionieren. Das große Problem entsteht, wenn Sie mit einem Produkt arbeitenIEnumerable<T>
, das mit Ertrag konstruiert wurde. In diesem Fall können Sie die EigenschaftenT
einer Iteration nicht mehr ändern und erwarten, dass sie vorhanden sind, wenn Sie sie erneut iterierenIEnumerable<T>
.quelle
Dies gilt, solange es sich nicht um eine Struktur handelt.
quelle
Vielleicht ist es für Sie interessant zu verstehen, dass es ab Version C # 7.3 möglich ist, Werte durch Referenz zu ändern, vorausgesetzt, die Current- Eigenschaft des Enumerators gibt einen Referenztyp zurück. Folgendes wäre gültig (wörtliche Kopie aus den MS-Dokumenten):
Span<int> storage = stackalloc int[10]; int num = 0; foreach (ref int item in storage) { item = num++; }
Weitere Informationen zu dieser neuen Funktion finden Sie unter C # foreach-Anweisung | Microsoft Docs .
quelle
Nun, ohne genau zu verstehen, was Sie unter "Durch Referenz iterieren" verstehen, kann ich nicht spezifisch mit Ja oder Nein antworten, aber ich kann sagen, dass unter der Oberfläche vorgeht, dass das .net-Framework eine "Enumerator" -Klasse für erstellt Jedes Mal, wenn der Client-Code ein foreach für die Lebensdauer des foreach aufruft, das einen Referenzzeiger in der Sammlung enthält, über die iteriert wird, und jedes Mal, wenn Ihr foreach iteriert, "liefert" ir ein Element und "erhöht" den Zeiger oder die Referenz in der Enumerator zum nächsten Punkt ...
Dies geschieht unabhängig davon, ob es sich bei den Elementen in der Sammlung, über die Sie iterieren, um Werttypen oder Referenztypen handelt.
quelle
obj ist ein Verweis auf ein Element in der Liste. Wenn Sie also dessen Wert ändern, bleibt es bestehen. Nun sollten Sie sich Sorgen machen, ob get_the_list (); erstellt eine tiefe Kopie der Liste oder gibt dieselbe Instanz zurück.
quelle
Ja, deshalb können Sie das aufzählbare Objekt auch nicht im Kontext der foreach-Anweisung ändern.
quelle