Ich war perplex, nachdem ich diesen Codeabschnitt ausgeführt hatte, in dem sich Strings zu verhalten scheinen, als wären sie Werttypen. Ich frage mich, ob der Zuweisungsoperator auf Werten wie Gleichheitsoperator für Strings arbeitet.
Hier ist der Code, den ich gemacht habe, um dieses Verhalten zu testen.
%Vor%Wenn ich im obigen Code eine benutzerdefinierte Klasse anstelle von Strings verwende, bekomme ich das erwartete Verhalten. Ich bezweifle, dass dies etwas mit der Unveränderlichkeit der Saite zu tun hat?
begrüßt Expertenmeinungen dazu.
Das ist die Syntax Zucker, die vom Compiler bereitgestellt wird. Eine genauere Darstellung dieser Aussage wäre:
%Vor%erklärt, wie a2 einfach einen Verweis auf ein neues String-Objekt erhält und Ihre Frage beantwortet. Der eigentliche Code ist stark optimiert, weil er so gebräuchlich ist. Es gibt einen dedizierten Opcode dafür in IL:
%Vor%String-Literale werden in einer Tabelle in der Assembly gesammelt. Dadurch kann der JIT-Compiler die Zuweisungsanweisung sehr effizient implementieren:
%Vor%Eine einzige Maschinencode-Anweisung kann das nicht übertreffen. Mehr noch: Eine sehr bemerkenswerte Konsequenz ist, dass das String-Objekt nicht auf dem Heap lebt. Der Garbage Collector kümmert sich nicht darum, da er erkennt, dass sich die Adresse der String-Referenz nicht im Heap befindet. Sie bezahlen also nicht einmal für den Sammlungsaufwand. Das kann ich nicht schlagen.
Beachten Sie auch, dass dieses Schema problemlos das Internieren von Strings ermöglicht. Der Compiler generiert einfach das gleiche LDSTR-Argument für ein identisches Literal.
Sie ändern nichts an dem Objekt, auf das a1 zeigt, sondern ändern das Objekt, auf das a1 zeigt.
Ihr Beispiel ersetzt "new Person {...}" durch ein String-Literal, aber das Prinzip ist dasselbe.
Der Unterschied kommt, wenn Sie Eigenschaften des Objekts ändern. Ändern Sie die Eigenschaft eines Werttyps, und es wird nicht im Original wiedergegeben.
Ändern Sie die Eigenschaft eines Referenztyps, und es wird im Original widergespiegelt.
ps. Entschuldigung wegen der Größe der Bilder, sie stammen nur von etwas, das ich herumliegen hatte. Das vollständige Set finden Sie unter Ссылка , das Werttypen, Referenztypen und Werttypen nach Wert und nach Werttypen abdeckt Referenz und Weitergabe von Referenztypen nach Wert und Verweis.
Immer wenn Sie
sehen %Vor%das ändert den Wert der Variablen - es nicht ändert den Inhalt des Objekts, auf das sich der Wert der Variablen bezieht.
Dieses Verhalten von string stimmt vollständig mit anderen Referenztypen überein und hat nichts mit Unveränderlichkeit zu tun. Zum Beispiel:
%Vor% Diese letzte Zeile ändert nichts an b1
- sie ändert nicht das Objekt, auf das sie verweist, oder den Inhalt des Objekts, auf das sie verweist. Es bewirkt nur, dass b2
auf eine neue StringBuilder
verweist.
Die einzige "Überraschung" hier ist, dass Strings in der Sprache eine besondere Unterstützung in Form von Literalen haben. Während wichtige Details wie das String-Interning (so dass dieselbe String-Konstante, die an mehreren Stellen in derselben Assembly auftritt, immer Verweise auf dasselbe Objekt liefert), hat dies keinen Einfluss auf die Bedeutung des Zuweisungsoperators.
Sie tun es nicht. Sie haben den Zeiger von a2
geändert, nicht das Objekt, auf das er gezeigt hat.
Wenn Sie Klassen verwenden und Ihr erwartetes Verhalten erhalten, müssen Sie eine Eigenschaft des Objekts festlegen, nicht seine Referenz.
Jede andere Klasse verhält sich genauso:
%Vor%Wenn ich mich richtig erinnere, hat Erik Lippert einmal auf SO geschrieben, dass dieses Verhalten so gewählt wurde, dass das Multi-Threading einfacher und sicherer ist. Wenn Sie eine Zeichenfolge in a1 speichern, wissen Sie, dass nur Sie diese ändern können. Es kann nicht von anderen Threads zum Beispiel geändert werden.
Der erste Grund ist String ist eine unveränderliche Klasse.
Ein Objekt wird als unveränderlich bezeichnet, wenn sein Wert nach der Erstellung nicht mehr geändert werden kann. Beispielsweise geben Methoden, die einen String zu ändern scheinen, tatsächlich einen neuen String zurück, der die Änderung enthält. Entwickler ändern Strings ständig in ihrem Code. Dies mag dem Entwickler als veränderlich erscheinen - ist es aber nicht. Was tatsächlich passiert ist, dass Ihre String-Variable / Objekt geändert wurde, um auf einen neuen String-Wert zu verweisen, der die Ergebnisse Ihres neuen String-Wertes enthält. Aus diesem Grund hat .NET die System.Text.StringBuilder-Klasse. Verwenden Sie die System.Text.StringBuilder-Klasse, wenn Sie den tatsächlichen Inhalt eines stringähnlichen Objekts wie in einer For- oder Foreach-Schleife stark ändern müssen.
Zum Beispiel:
Zeichenfolge x = 123;
Wenn Sie x = x + abc tun, weist es einen neuen Speicherort für 123 und abc zu. Fügt dann die zwei Zeichenfolgen hinzu und platziert die berechneten Ergebnisse in einem neuen Speicherort und zeigt darauf x.
wenn Sie verwenden System.Text.StringBuilder sb neu System.Text.StringBuilder (123); sb.Append (abc); x sb.ToString ();
stringbuilder ist eine veränderbare Klasse. Es fügt nur die Zeichenfolge zum gleichen Speicherort hinzu. Auf diese Weise ist die Bearbeitung von Strings schneller.
Eine Zeichenfolge ist ein Objekt vom Typ String, dessen Wert Text ist. Intern wird der Text als eine schreibgeschützte Sammlung von Char-Objekten gespeichert, von denen jedes ein Unicode-Zeichen darstellt, das in UTF-16 codiert ist.
Es gibt drei semantische Arten von Entitäten:
Wenn man eine Kopie X einer veränderbaren Referenz Y macht und dann etwas mit der Kopie macht, beeinflusst jede Mutation, die an X durchgeführt wird, Y und umgekehrt, da X und Y beide auf dasselbe Objekt verweisen. Wenn Sie dagegen eine Kopie XX einer veränderbaren Werttypinstanz YY erstellen, wirken sich Änderungen an XX weder auf YY noch umgekehrt aus.
Da der einzige semantische Unterschied zwischen Referenztypen und Werttypen das Verhalten ist, wenn sie nach dem Kopieren geändert werden, sind unveränderliche Referenztypen semantisch identisch mit unveränderlichen Werttypen. Dies soll nicht bedeuten, dass es manchmal nicht zu erheblichen Leistungsvorteilen kommt, wenn man übereinander verwendet.
(*) Bedeutung Werttypen, die teilweise geändert werden können, ohne vollständig ersetzt zu werden. Punkt ist zum Beispiel veränderbar, weil man einen Teil davon ändern kann, ohne das Ganze lesen und neu schreiben zu müssen. Im Gegensatz dazu ist Int32 unveränderlich, da es (zumindest vom "sicheren" Code) nicht möglich ist, eine Änderung an einem Int32 vorzunehmen, ohne das Ganze neu zu schreiben.