Warum sich Strings wie ValueType verhalten

8

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.

    
AbrahamJP 26.05.2010, 09:38
quelle

7 Antworten

7
%Vor%

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.

    
Hans Passant 26.05.2010, 10:36
quelle
23

Sie ändern nichts an dem Objekt, auf das a1 zeigt, sondern ändern das Objekt, auf das a1 zeigt.

a = neue Person (); b = a; b = neue Person (); http://dev.morethannothing.co.uk/valuevsreference/Valuevs.Reference3.png

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.

a = neue Person (); b = a; b.Name = ...; http://dev.morethannothing.co.uk/valuevsreference/Valuevs.Reference1.png

Ändern Sie die Eigenschaft eines Referenztyps, und es wird im Original widergespiegelt.

a = neue Person (); b = a; b.Name = ...; http://dev.morethannothing.co.uk/valuevsreference/Valuevs.Reference2.png

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.

    
ICR 26.05.2010 10:03
quelle
9

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.

    
Jon Skeet 26.05.2010 09:45
quelle
7

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%     
Kobi 26.05.2010 09:40
quelle
0

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.

    
Petar Repac 26.05.2010 12:51
quelle
0

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.

    
kbmanju 27.05.2010 11:50
quelle
0

Es gibt drei semantische Arten von Entitäten:

  1. Veränderbare Referenztypen
  2. Veränderbare Werttypen (*)
  3. Unveränderliche Typen

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.

    
supercat 26.07.2011 15:25
quelle

Tags und Links