Ich weiß, dass Werttypen unveränderlich sein sollten, aber das ist nur ein Vorschlag, keine Regel, oder? Warum kann ich so etwas nicht tun:
struct MyStruct
{
public string Name { get; set; }
}
public class Program
{
static void Main(string[] args)
{
MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } };
foreach (var item in array)
{
item.Name = "3";
}
//for (int i = 0; i < array.Length; i++)
//{
// array[i].Name = "3";
//}
Console.ReadLine();
}
}
Die foreach-Schleife im Code wird nicht kompiliert, während die kommentierte for-Schleife einwandfrei funktioniert. Die Fehlermeldung:
Mitglieder von 'item' können nicht geändert werden, da es sich um eine 'foreach-Iterationsvariable' handelt.
Warum ist das so?
c#
foreach
immutability
value-type
Cui Pengfei 崔鹏飞
quelle
quelle
foreach
Schleifenvariable nicht geändert werden kann, aber ich habe nie wirklich gelernt, warum !Antworten:
Da foreach einen Enumerator verwendet und Enumeratoren die zugrunde liegende Auflistung nicht ändern können , können sie jedoch alle Objekte ändern, auf die von einem Objekt in der Auflistung verwiesen wird . Hier kommt die Semantik vom Typ Wert und Referenz ins Spiel.
Bei einem Referenztyp, dh einer Klasse, ist die gesamte Sammlung, die gespeichert wird, eine Referenz auf ein Objekt. Als solches berührt es niemals eines der Mitglieder des Objekts und könnte sich auch nicht weniger um sie kümmern. Eine Änderung am Objekt berührt die Sammlung nicht.
Andererseits speichern Werttypen ihre gesamte Struktur in der Sammlung. Sie können seine Mitglieder nicht berühren, ohne die Sammlung zu ändern und den Enumerator ungültig zu machen.
Darüber hinaus gibt der Enumerator eine Kopie des Werts in der Auflistung zurück. In einem Ref-Typ bedeutet dies nichts. Eine Kopie einer Referenz ist dieselbe Referenz, und Sie können das referenzierte Objekt beliebig ändern, wobei sich die Änderungen außerhalb des Gültigkeitsbereichs ausbreiten. Auf der anderen Seite bedeutet ein Werttyp, dass Sie nur eine Kopie des Objekts erhalten, und daher werden Änderungen an dieser Kopie niemals weitergegeben.
quelle
Es ist ein Vorschlag in dem Sinne, dass nichts Sie davon abhält, ihn zu verletzen, aber es sollte wirklich viel mehr Gewicht erhalten als "es ist ein Vorschlag". Zum Beispiel aus den Gründen, die Sie hier sehen.
Werttypen speichern den tatsächlichen Wert in einer Variablen, nicht in einer Referenz. Das bedeutet, dass Sie den Wert in Ihrem Array haben und eine Kopie dieses Werts in
item
, keine Referenz. Wenn Sie die Werte in ändern könntenitem
, würde dies nicht auf den Wert im Array übertragen, da es sich um einen völlig neuen Wert handelt. Deshalb ist es nicht erlaubt.Wenn Sie dies tun möchten, müssen Sie das Array nach Index durchlaufen und dürfen keine temporäre Variable verwenden.
quelle
Strukturen sind Werttypen.
Klassen sind Referenztypen.
ForEach
Konstrukt verwendet IEnumerator von IEnumerable Datentypelementen. In diesem Fall sind Variablen in dem Sinne schreibgeschützt, dass Sie sie nicht ändern können. Da Sie einen Werttyp haben, können Sie keinen enthaltenen Wert ändern, da er denselben Speicher verwendet.C # lang spec Abschnitt 8.8.4:
Um dies zu beheben, verwenden Sie die Strukturklasse insted.
class MyStruct { public string Name { get; set; } }
Bearbeiten: @CuiPengFei
Wenn Sie
var
impliziten Typ verwenden, ist es für den Compiler schwieriger, Ihnen zu helfen. Wenn Sie es verwendenMyStruct
würden, würde es Ihnen im Falle einer Struktur sagen, dass es schreibgeschützt ist. Bei Klassen ist der Verweis auf das Element schreibgeschützt, sodass Sie nichtitem = null;
in die Schleife schreiben können , sondern die veränderbaren Eigenschaften ändern können.Sie können auch verwenden (wenn Sie verwenden möchten
struct
):MyStruct[] array = new MyStruct[] { new MyStruct { Name = "1" }, new MyStruct { Name = "2" } }; for (int index=0; index < array.Length; index++) { var item = array[index]; item.Name = "3"; }
quelle
HINWEIS: Laut Adams Kommentar ist dies nicht die richtige Antwort / Ursache des Problems. Es ist jedoch immer noch etwas, an das man denken sollte.
Von MSDN . Sie können die Werte nicht ändern, wenn Sie den Enumerator verwenden. Dies ist im Wesentlichen das, was foreach tut.
quelle
foreach
(das ist nicht das, worüber der MSDN-Artikel spricht), da dadurch das Mitglied der Sammlung und nicht die Sammlung selbst geändert wird. Hier geht es um die Semantik von Werttyp und Referenztyp.Machen Sie MyStruct zu einer Klasse (anstelle von struct), und Sie können dies tun.
quelle
Ich denke, dass Sie die Antwort unten finden können.
Foreach struct seltsamen Kompilierungsfehler in C #
quelle
Werttypen werden nach Wert aufgerufen . Kurz gesagt, wenn Sie die Variable auswerten, wird eine Kopie davon erstellt. Selbst wenn dies zulässig wäre, würden Sie eine Kopie bearbeiten und nicht die ursprüngliche Instanz von
MyStruct
.foreach
ist schreibgeschützt in C # . Für Referenztypen (Klassen) ändert sich nicht viel, da nur die Referenz schreibgeschützt ist, sodass Sie weiterhin Folgendes tun können:MyClass[] array = new MyClass[] { new MyClass { Name = "1" }, new MyClass { Name = "2" } }; foreach ( var item in array ) { item.Name = "3"; }
Bei Werttypen (struct) ist das gesamte Objekt jedoch schreibgeschützt, was zu dem führt, was Sie erleben. Sie können das Objekt im foreach nicht anpassen.
quelle