Was ist die Verwendung von System.String.Copy in .NET?

71

Ich fürchte, das ist eine sehr dumme Frage, aber mir muss etwas fehlen.

Warum möchte man vielleicht String.Copy (string) verwenden ?

Die Dokumentation sagt die Methode

Erstellt eine neue Instanz von String mit demselben Wert wie ein angegebener String.

Da Zeichenfolgen in .NET unveränderlich sind, bin ich mir nicht sicher, welchen Nutzen diese Methode hat, da ich das denke

 string copy = String.Copy(otherString);

würde für alle praktischen Zwecke das gleiche Ergebnis liefern wie

 string copy = otherString;

Das heißt, abgesehen von der internen Buchhaltung und der Tatsache, dass das Kopieren nicht ReferenceEqualsauf otherString erfolgt, gibt es keine beobachtbaren Unterschiede. String ist eine unveränderliche Klasse, deren Gleichheit auf Wert und nicht auf Identität basiert. (Dank an @Andrew Hare für den Hinweis, dass meine ursprüngliche Formulierung nicht präzise genug war, um anzuzeigen, dass ich einen Unterschied zwischen Copying und nicht erkannte , aber besorgt über den wahrgenommenen Mangel an nützlichem Unterschied war.)

Wenn ein nullArgument übergeben wird, löst Copy natürlich ein aus ArgumentNullException, und die "neue Instanz" verbraucht möglicherweise mehr Speicher. Letzteres scheint kaum ein Vorteil zu sein, und ich bin mir nicht sicher, ob der Null-Check groß genug ist, um eine vollständige Kopiermethode zu rechtfertigen.

Vielen Dank.

Blair Conrad
quelle
1
Nun

Antworten:

45

Mit String.Copyordnen Sie tatsächlich neuen Speicher zu und kopieren die Zeichen von einer Zeichenfolge in eine andere. Sie erhalten eine völlig neue Instanz, anstatt dass beide Variablen dieselbe Instanz sind. Dies kann von Bedeutung sein, wenn Sie die Zeichenfolge mit nicht verwaltetem Code verwenden, der sich direkt mit den Speicherorten befasst und die Zeichenfolge mutieren kann.

Tvanfosson
quelle
2
Dies ist zwar das erste, was mir in den Sinn gekommen ist, aber es klingt sicher wie ein Fehler, der darauf wartet, passiert zu werden. Wenn Sie Interop mit nicht verwaltetem Code ausführen, der eine Zeichenfolge ändert, sollten Sie wahrscheinlich stattdessen einen StringBuilder verwenden.
Michael Burr
4
Klingt nach einem Fehlermuster, wenn Sie versucht haben, ein unveränderliches Objekt zu ändern.
Dennis C
1
Ich habe nie gesagt, dass ich das tun würde, und ich kann mir keine Beispiele vorstellen, bei denen ich versucht wäre, dies zu tun. Wenn ich andererseits nicht verwalteten Code verwenden müsste, der sich mit Zeichenfolgen befasst, und ihn an Ort und Stelle ändern müsste, würde ich wahrscheinlich eine Kopie meiner Zeichenfolge erstellen und diese übergeben, um die Fehler zu vermeiden.
Tvanfosson
1
Ja, das ist das Stück, das mir gefehlt hat - was passiert, wenn Sie Ihr Gedächtnis an externen Code weitergeben. Bei der Arbeit übergeben wir Strings als nicht modifizierbare Parameter an nicht verwalteten Code, verwenden jedoch StringBuffers, wenn wir erwarten, dass Dinge beschädigt werden - es wäre mir nicht in den Sinn gekommen, String dafür zu verwenden. Vielen Dank.
Blair Conrad
3
Ich würde sagen, diese Antwort bedeutet, dass String.Copy nicht sinnvoll verwendet werden kann!
Daniel Earwicker
25

Hier ist ein Teil des Puzzles. Es erklärt nicht, warum Sie es tun möchten, aber es hilft, einen funktionalen Unterschied zu erklären.

Wenn Sie die Zeichenfolge mit dem fixedSchlüsselwort anheften, kann der Inhalt geändert werden. Ich kann mir keine Situation vorstellen, in der Sie dies tun möchten, aber es ist möglich.

string original = "Hello World";
string refCopy = original;
string deepCopy = String.Copy(original);

fixed(char* pStr = original)
{
   *pStr = 'J';
}

Console.WriteLine(original);
Console.WriteLine(refCopy);
Console.WriteLine(deepCopy);

Ausgabe:

Jello World
Jello World
Hello World
Kennet Belenky
quelle
1
Das ist würdig und schrecklich. +1 für das Beispiel.
Patrick M
fantastische Antwort.
Kalyan
Es ist nicht würdig, wird aber wahrscheinlich normalerweise missbraucht. Aus Sicherheitsgründen möchten Sie beispielsweise beim Umgang mit SecureString oder beim kurzen Speichern vertraulicher Daten im Speicher möglicherweise den Speicherinhalt der Zeichenfolge löschen, anstatt auf die Speicherbereinigung zu warten. Es macht es weitaus schwieriger, den Speicherinhalt zu sichern und die Daten abzufangen, die nur dort sitzen und darauf warten, bereinigt zu werden, wenn Sie sie sofort löschen, wenn sie nicht mehr benötigt werden.
Michael Brown
21

String.Copygibt ein neues zurück Stringund liefert nicht die gleichen Ergebnisse wie

String copy = otherString;

Versuche dies:

using System;

class Program
{
    static void Main()
    {
        String test = "test";
        String test2 = test;
        String test3 = String.Copy(test);

        Console.WriteLine(Object.ReferenceEquals(test, test2));
        Console.WriteLine(Object.ReferenceEquals(test, test3));

        Console.ReadLine();
    }
}

Wenn Sie test2 = testdiese Referenzen festlegen, verweisen Sie auf dasselbe String. Die CopyFunktion gibt eine neue StringReferenz zurück, die denselben Inhalt hat, jedoch als anderes Objekt auf dem Heap.


Bearbeiten: Es gibt viele Leute, die ziemlich verärgert sind, dass ich die Frage des OP nicht beantwortet habe. Ich glaube, dass ich die Frage beantwortet habe, indem ich eine falsche Prämisse in der Frage selbst korrigiert habe. Hier ist eine analoge (wenn nicht zu vereinfachte) Frage und Antwort, die hoffentlich meinen Standpunkt verdeutlicht:

Frage:

Ich habe beobachtet, dass mein Auto zwei Türen hat, eine auf jeder Seite des Autos. Ich glaube, es ist wahr, dass ich unabhängig davon, welche Tür ich benutze, auf dem Fahrersitz sitzen werde. Was ist der Zweck der anderen Tür?

Antworten:

Eigentlich ist es nicht wahr, dass Sie auf dem Fahrersitz landen, wenn Sie eine der Türen benutzen. Wenn Sie die Fahrertür benutzen, landen Sie auf dem Fahrersitz und wenn Sie die Beifahrertür benutzen, landen Sie auf dem Beifahrersitz.

In diesem Beispiel könnten Sie argumentieren, dass die Antwort nicht wirklich eine Antwort ist, da die Frage lautete: "Was ist der Zweck der Beifahrertür?". Aber da diese Frage ausschließlich auf einem Missverständnis der Funktionsweise der Türen beruhte, folgt daraus nicht, dass die Widerlegung der Prämisse durch Abzug neues Licht auf den Zweck der anderen Tür werfen wird?

Andrew Hare
quelle
Die Frage war nicht "Was macht es", sondern "Warum sollte ich es benutzen?"
Peter Morris
Ich denke, der Punkt war: "Was nützt es, unveränderliche Informationen zu duplizieren?" anstatt es wiederzuverwenden.
PhiLho
Die gesamte Frage basierte auf der falschen Prämisse, dass "string one = String.Copy (two);" und "string one = two;" sind äquivalente Aussagen. Ich habe nur darauf hingewiesen, dass das Poster falsch war.
Andrew Hare
OP war "Warum sollte man String.Copy (string) verwenden wollen?" OP erkennt, dass es eine Kopie erstellt, aber was nützt das, wenn .Net-Zeichenfolgen unveränderlich sind?
Abelenky
Wenn das OP dies erkennt, warum enthält die Frage dann die folgende Anweisung: "string copy = String.Copy (otherString); würde das gleiche Ergebnis liefern wie string copy = otherString;"?
Andrew Hare
9

Eine schnelle Suche in der BCL nach .NET 4.0 zeigt, dass die string.CopyMethode an etwa einem halben Dutzend Stellen aufgerufen wird. Die Verwendungen fallen in ungefähr diese Kategorien:

  1. Für die Interaktion mit nativen Funktionen, die die an sie übergebenen Zeichenfolgen beschädigen können. Wenn Sie die P / Invoke-Deklaration nicht beeinflussen und die aufgerufene Funktion nicht reparieren können, string.Copyist dies die beste Wahl.

  2. Für Situationen, in denen die Zeichenfolge aus Leistungsgründen direkt geändert wird. Wenn Sie nur wenige Zeichen in einer möglicherweise langen Zeichenfolge in Kleinbuchstaben konvertieren müssen, können Sie dies nur tun, ohne die Zeichenfolge zweimal zu kopieren und zusätzlichen Müll zu erstellen, indem Sie sie mutieren.

  3. An Orten, an denen es nicht notwendig erscheint. Es ist durchaus möglich, dass einige Programmierer eher an Java- oder C ++ - Zeichenfolgen gewöhnt sind und nicht erkennen, dass das Kopieren einer Zeichenfolge in C # selten nützlich ist.

Gabe
quelle
8
string a = "abc";
string b = String.Copy(a);

Monitor.Enter(a); // not the same as Monitor.Enter(b);

jedoch

string c = "123";
string d = c;
Monitor.Enter(c); // the same as Monitor.Enter(d);

Ich denke, es ist der Vollständigkeit halber so, wie es irgendjemand interessieren wird.


Ebenfalls

StringBuilder sb = new StringBuilder(100);
sb.Append("abc");
string a = sb.ToString();
string b = String.Copy(a);

Ich denke a, dann wird mehr RAM belegt b, da aauf den Puffer der Größe 100 verwiesen wird, den der StringBuildererstellt hat. (Schauen Sie sich das Innere der StringBuilder.ToString()Methode an.)


Ich denke, StringBuilderdass die Verwendung des String.Copy().NET-Frameworks StringBuilderden Inhalt des .NET-Frameworks verändert string. A stringist also nicht immer unveränderlich.

Ian Ringrose
quelle
StringBuilder muss niemals einen String ändern. Intern könnte eine Liste <char> verwendet werden, deren Größe effizient zunimmt, und dann würde die ToString-Methode eine Zeichenfolge aus dem Inhalt der Liste zuweisen.
Daniel Earwicker
1

Zusätzlich zu dem, was tvanfosson gesagt hat (ich glaube nicht, dass Sie über nicht verwalteten Code auf den von einer verwalteten Zeichenfolge verwendeten Puffer zugreifen können ... Ich weiß, dass es zumindest schwierig wäre), glaube ich, dass es einen Unterschied geben kann, wenn die Zeichenfolge ist Wird als Objekt zum Sperren der Multithread-Funktionalität verwendet.

Zum Beispiel...

using System;

public class Class1
{
    string example1 = "example";
    string example2 = example1;

    public void ExampleMethod1()
    {
        lock (example1)
        {
            Console.WriteLine("Locked example 1");
            //do stuff...
        }
    }

    public void ExampleMethod2()
    {
        lock (example2)
        {
            Console.WriteLine("Locked example 2");
            //do stuff
        }
    }
}

Ich glaube, wenn die beiden Beispielmethoden parallel ausgeführt werden, sperren sie dasselbe Objekt und daher kann eine nicht ausgeführt werden, während sich die andere in ihrem Sperrblock befindet.

Wenn Sie es jedoch ändern ...

using System;

public class Class1
{
    string example1 = "example";
    string example2 = string.Copy(example1);

    public void ExampleMethod1()
    {
        lock (example1)
        {
            Console.WriteLine("Locked example 1");
            //do stuff...
        }
    }

    public void ExampleMethod2()
    {
        lock (example2)
        {
            Console.WriteLine("Locked example 2");
            //do stuff
        }
    }
}

Dann glaube ich, dass sie nur die Ausführung anderer Threads blockieren, die dieselbe Methode ausführen (dh alle Threads, die ExampleMethod1 ausführen, werden gesperrt, bis jeder abgeschlossen ist, aber sie werden Threads, die ExampleMethod2 ausführen, nicht stören).

Ich bin mir nicht sicher, ob dies ein nützlicher Unterschied ist, da es bessere Mechanismen für die Synchronisation gibt (ich denke nicht, dass das Sperren von Zeichenfolgen eine sehr gute Idee ist).


quelle
0
string a = "test";
string b = a;
//Object.ReferenceEquals(a,b) is true
a += "more";
//Object.ReferenceEquals(a,b) is now false !

Auto-Change-Erkennung?

wiwulo
quelle
3
Nein, durch Anhängen an die Zeichenfolge wird eine neue Instanz einer Zeichenfolge erstellt (da Zeichenfolgen unveränderlich sind). Nach dem Anhängen zeigt "a" auf eine andere Instanz.
e11s
-1

Ich bin nicht sicher, wie String in .NET implementiert wird, aber ich denke, Java ist eine gute Referenz.

In Java macht new String (str) auch was String.copy (str); Weisen Sie einen neuen String mit demselben Wert zu.

Es scheint nutzlos, aber es ist sehr nützlich bei der Speicheroptimierung.

String enthält ein char [] mit Offset und Länge in der Implementierung. Wenn Sie so etwas wie einen Teilstring ausführen, wird keine Speicherkopie erstellt, sondern eine neue String-Instanz zurückgegeben, die dasselbe Zeichen [] verwendet. In vielen Fällen spart dieses Muster viel Speicherkopie und Zuordnung. Wenn Sie jedoch ein kleines Stück innerhalb eines langen großen Strings unterteilen. Es wird immer noch auf großes Zeichen [] verwiesen, selbst wenn der ursprüngliche große String GC sein kann.

String longString = // read 1MB text from a text file
String memoryLeak = largeString.substring(100,102); 
largeString=null;
// memoryLeak will be sized 1MB in the memory
String smaller = new String(largeString.substring(100,102));
// smaller will be only few bytes in the memory

Es kann das neue String-Objekt zwingen, sein eigenes Zeichen [] zuzuweisen, um versteckte Speicherverluste / -verschwendung zu verhindern.

Dennis C.
quelle
So funktioniert das nicht. Nur anzunehmen, dass .NET dieselbe Implementierung wie Java hat, ist auch nicht richtig. Darüber hinaus hat die Antwort nichts mit der Frage zu tun. Was passiert, wenn Sie .Substring (100, 102): Ein neuer String mit der Länge 102 wird erstellt, der die Zeichen 100 ... 201
Sander Rijken