Frage

In .NET sind Zeichenfolgen unveränderlich und Referenztypvariablen. Dies ist für neuere .NET -Entwickler häufig eine Überraschung, die sie aufgrund ihres Verhaltens für Werttypobjekte verwechseln können. Anders als die Praxis der Verwendung StringBuilder für eine lange Verkettung ESP. Gibt es in Schleifen einen Grund in der Praxis, dass man diese Unterscheidung kennen muss?

Welche realen Szenarien werden geholfen oder vermieden, indem sie die Wertschöpfungsunterscheidung in Bezug auf .NET-Saiten im Vergleich zu Verständnis/Missverständnis als Werttypen haben?

War es hilfreich?

Lösung

Das Design von stringS war absichtlich so, dass Sie sich als Programmierer nicht zu viel Sorgen machen müssen. In vielen Situationen bedeutet dies, dass Sie einfach zu viel komplizierte Konsequenzen zuweisen, bewegen, kopieren, ändern, ohne zu viel zu denken, wenn eine andere Referenz auf Ihre Zeichenfolge vorhanden war und gleichzeitig geändert würde (wie bei Objektreferenzen).

String -Parameter in einem Methodenaufruf

(Bearbeiten: Dieser Abschnitt wurde später hinzugefügt)
Wenn Saiten an eine Methode weitergegeben werden, werden sie mit Referenz übergeben. Wenn sie nur im Methode -Körper gelesen werden, passiert nichts Besonderes. Wenn sie jedoch geändert werden, wird eine Kopie erstellt und die temporäre Variable im Rest der Methode verwendet. Dieser Prozess wird genannt Kopieren auf dem Schreiben.

Was Junioren beunruhigen, ist, dass sie daran gewöhnt sind, dass Objekte Referenzen sind und sie in einer Methode geändert werden, die den übergebenen Parameter ändert. Um dasselbe mit Saiten zu tun, müssen sie das verwenden ref Stichwort. Dadurch kann die String -Referenz geändert und zur Aufruffunktion zurückgegeben werden. Wenn Sie dies nicht tun, kann die Zeichenfolge nicht durch den Method -Körper geändert werden:

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

Auf StringBuilder

Diese Unterscheidung ist wichtig, aber Anfängerprogrammierer sind normalerweise besser dran, nicht zu viel darüber zu wissen. Verwendung StringBuilder Wenn Sie viel "Gebäude" durchführen, ist es gut, aber oft hat Ihre Bewerbung viel mehr Fische zum Braten und den kleinen Leistungsgewinn von StringBuilder Ist vernachlässigbar. Seien Sie vorsichtig mit Programmierern, die Ihnen das sagen alle String Manipulation sollte mit StringBuilder durchgeführt werden.

In einer sehr groben Faustregel: StringBuilder hat einige Erstellungskosten, aber das Anhängen ist billig. Saite hat eine billige Kreationskosten, aber die Verkettung ist relativ teuer. Der Wendepunkt beträgt je nach Größe etwa 400-500 Verkettungen: Danach wird Stringbuilder effizienter.

Mehr zu StringBuilder vs String Performance

Bearbeiten: Basierend auf einem Kommentar von Konrad Rudolph habe ich diesen Abschnitt hinzugefügt.

Wenn Sie die vorherige Faustregel wundern lassen, sollten Sie die folgenden etwas detaillierteren Erklärungen betrachten:

  • StringBuilder mit vielen kleinen Zeichenfolgen findet eine String -Verkettung ziemlich schnell (30, 50 Anhänge), aber bei 2 µs ist sogar 100% Leistungsgewinn oft vernachlässigbar (sicher für einige seltene Situationen).
  • StringBuilder mit einigen großen String -Anhängen (80 Zeichen oder größere Saiten) überträgt die String -Verkettung erst nach Tausenden, manchmal Hundertstel von Tausenden Iterationen, und der Unterschied beträgt oft nur wenige Wahrnehmungen.
  • Mischungsstringaktionen (ersetzen, einfügen, substring, regex usw.) häufig mit StringBuilder oder String -Verkettung gleich;
  • Die String -Verkettung von Konstanten kann vom Compiler, dem CLR oder dem JIT optimiert werden, es kann nicht für StringBuilder;
  • Code mischt oft Verkettung +, StringBuilder.Append, String.Format, ToString und andere String -Operationen, die StringBuilder in solchen Fällen verwenden, ist kaum effektiv.

Also wann ist es effizient? In Fällen, in denen viele kleine Zeichenfolgen angehängt werden, dh beispielsweise Daten in einer Datei und wenn Sie die "geschriebenen" Daten einmal "geschrieben" an StringBuilder ändern müssen. Und in Fällen, in denen viele Methoden etwas anhängen müssen, da StringBuilder ein Referenztyp ist und Zeichenfolgen kopiert werden, wenn sie geändert werden.

Auf interned Saiten

Ein Problem steigt - nicht nur mit Junior -Programmierern -, wenn sie versuchen, einen Referenzvergleich durchzuführen und herauszufinden, dass das Ergebnis manchmal wahr ist, und manchmal in den gleichen Situationen falsch ist. Was ist passiert? Wenn die Saiten vom Compiler interniert wurden und dem globalen statischen internierten Pool von Saiten hinzugefügt wurden, kann der Vergleich zwischen zwei Zeichenfolgen auf dieselbe Speicheradresse hinweisen. Wenn (Referenz!) Zwei gleiche Zeichenfolgen, einer Praktikant und einer, vergleicht, ergibt sich falsch. Verwenden = Vergleich, oder Equals und spiele nicht mit ReferenceEquals Beim Umgang mit Saiten.

Auf String.Empty

In derselben Liga passt zu einem seltsamen Verhalten, das manchmal beim Gebrauch auftritt String.Empty: die statische String.Empty ist immer interniert, aber eine Variable mit einem zugewiesenen Wert ist nicht. Standardmäßig wird der Compiler jedoch zugewiesen String.Empty und verweisen Sie auf die gleiche Speicheradresse. Ergebnis: Eine veränderliche Zeichenfolgevariable im Vergleich zu ReferenceEquals, kehrt wahr, während Sie stattdessen möglicherweise false erwarten.

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

Ausführlich

Sie haben im Grunde gefragt, welche Situationen der Uneingeweihten auftreten können. Ich denke, mein Punkt läuft auf das Vermeiden hinaus object.ReferenceEquals Weil es nicht vertrauenswürdig werden kann, wenn es mit Saiten verwendet wird. Der Grund dafür ist, dass die String -Einbringer verwendet wird, wenn die Zeichenfolge im Code konstant ist, aber nicht immer. Sie können sich nicht auf dieses Verhalten verlassen. Obwohl String.Empty und "" sind immer interniert, es ist nicht der Fall, wenn der Compiler der Ansicht ist, dass der Wert veränderlich ist. Unterschiedliche Optimierungsoptionen (Debug vs Release und andere) führen zu unterschiedlichen Ergebnissen.

Wann tun du brauchst ReferenceEquals ohnehin? Bei Objekten macht es Sinn, aber mit Saiten nicht. Bringen Sie jedem mit Strings bei, um seine Verwendung zu vermeiden, es sei denn, er versteht auch unsafe und festgesteckte Objekte.

Leistung

Wenn die Leistung wichtig ist, können Sie feststellen, dass die Saiten tatsächlich sind nicht unveränderlich und das Verwendung StringBuilder ist nicht Immer der schnellste Ansatz.

Viele der hier verwendeten Informationen sind detailliert In diesem ausgezeichneten Artikel über Strings, zusammen mit einem "Wie man" zum Manipulieren von Saite In-Place (veränderliche Zeichenfolgen).

Aktualisieren: Code -Beispiel hinzugefügt
Aktualisieren: Hinzufügen von Abschnitt "Tiefe" (hoffe, jemand findet das nützlich;)
Aktualisieren: Einige Links fügten hinzu, fügte Abschnitt zu String -Params hinzu
Aktualisieren: Die Schätzung für den Wechsel von Strings zu StringBuilder hinzugefügt, um zu wechseln
Aktualisieren: Nach einer Bemerkung von Konrad Rudolph einen zusätzlichen Abschnitt über StringBuilder vs String -Leistung hinzugefügt

Andere Tipps

Die einzige Unterscheidung, die für die meisten Code wirklich wichtig ist, ist die Tatsache, dass null Kann String -Variablen zugewiesen werden.

Eine unveränderliche Klasse wirkt wie ein Werttyp in allen gemeinsamen Situationen, und Sie können eine Menge Programmierung machen, ohne sich viel um den Unterschied zu kümmern.

Wenn Sie ein wenig tiefer graben und sich um die Leistung kümmern, die Sie für die Unterscheidung echt verwenden. Zum Beispiel, um zu wissen, dass das Übergeben einer Zeichenfolge als Parameter an eine Methode so wirkt, als ob eine Kopie der Zeichenfolge erstellt wird, findet das Kopieren nicht tatsächlich statt. Dies könnte eine Überraschung für Menschen sein, die an Sprachen gewöhnt sind, in denen Zeichenfolgen tatsächlich Werttypen sind (wie VB6?), Und viele Saiten als Parameter nicht gut für die Leistung zu sein.

Saite ist eine besondere Rasse. Sie sind Referenztypen, aber von den meisten Codierern als Werttyp verwendet. Indem es es unveränderlich macht und den Praktikumspool verwendet, optimiert es die Speicherverbrauch, die bei einem reinen Werttyp enorm sein wird.

Weitere Lesungen hier:
C# .NET String -Objekt ist wirklich durch Referenz? auf so
String.Intern -Methode auf msdn
String (C# Referenz) auf MSDN

Aktualisieren:
Bitte beziehen Sie sich auf abel'S Kommentar zu diesem Beitrag. Es korrigierte meine irreführende Aussage.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top