Ich weiß, dass "string" in C # ein Referenztyp ist. Dies ist auf MSDN. Dieser Code funktioniert jedoch nicht wie gewünscht:
class Test
{
public static void Main()
{
string test = "before passing";
Console.WriteLine(test);
TestI(test);
Console.WriteLine(test);
}
public static void TestI(string test)
{
test = "after passing";
}
}
Die Ausgabe sollte "vor dem Übergeben" "nach dem Übergeben" sein, da ich die Zeichenfolge als Parameter übergebe und es sich um einen Referenztyp handelt. Die zweite Ausgabeanweisung sollte erkennen, dass sich der Text in der TestI-Methode geändert hat. Ich erhalte jedoch "vor dem Übergeben" "vor dem Übergeben", was den Anschein erweckt, dass es nach Wert und nicht nach Referenz übergeben wird. Ich verstehe, dass Strings unveränderlich sind, aber ich sehe nicht, wie das erklären würde, was hier vor sich geht. Was vermisse ich? Vielen Dank.
Antworten:
Der Verweis auf die Zeichenfolge wird als Wert übergeben. Es gibt einen großen Unterschied zwischen der Übergabe einer Referenz als Wert und der Übergabe eines Objekts als Referenz. Es ist bedauerlich, dass in beiden Fällen das Wort "Referenz" verwendet wird.
Wenn Sie tun die Zeichenfolge Verweis übergeben durch Verweis, wird es wie erwartet:
Jetzt müssen Sie unterscheiden, ob Sie Änderungen an dem Objekt vornehmen, auf das sich eine Referenz bezieht, oder ob Sie eine Variable (z. B. einen Parameter) ändern, damit sie sich auf ein anderes Objekt bezieht. Wir können keine Änderungen an einem String vornehmen, da Strings unveränderlich sind, aber wir können dies
StringBuilder
stattdessen mit einem demonstrieren :Weitere Informationen finden Sie in meinem Artikel zur Parameterübergabe .
quelle
referenced
das als Ihre Antwort habenWenn wir die Frage beantworten müssen: String ist ein Referenztyp und verhält sich wie eine Referenz. Wir übergeben einen Parameter, der einen Verweis enthält, nicht den tatsächlichen String. Das Problem liegt in der Funktion:
Der Parameter
test
enthält einen Verweis auf die Zeichenfolge, ist jedoch eine Kopie. Wir haben zwei Variablen, die auf die Zeichenfolge zeigen. Und da alle Operationen mit Zeichenfolgen tatsächlich ein neues Objekt erstellen, erstellen wir unsere lokale Kopie so, dass sie auf die neue Zeichenfolge verweist. Die ursprünglichetest
Variable wird jedoch nicht geändert.Die vorgeschlagenen Lösungen für
ref
die Funktionsdeklaration und den Aufruf funktionieren, da wir den Wert dertest
Variablen nicht übergeben, sondern nur einen Verweis darauf übergeben. Änderungen innerhalb der Funktion spiegeln somit die ursprüngliche Variable wider.Ich möchte am Ende wiederholen: String ist ein Referenztyp, aber da er unveränderlich ist, erstellt die Zeile
test = "after passing";
tatsächlich ein neues Objekt und unsere Kopie der Variablentest
wird so geändert, dass sie auf den neuen String zeigt.quelle
Wie andere angegeben haben, ist der
String
Typ in .NET unveränderlich und seine Referenz wird als Wert übergeben.Sobald diese Zeile im Originalcode ausgeführt wird:
dann bezieht
test
sich nicht mehr auf das ursprüngliche Objekt. Wir haben ein neuesString
Objekt erstellt undtest
dieses Objekt auf dem verwalteten Heap referenziert.Ich habe das Gefühl, dass viele Leute hier stolpern, da es keinen sichtbaren formalen Konstruktor gibt, der sie daran erinnert. In diesem Fall geschieht dies hinter den Kulissen, da der
String
Typ Sprachunterstützung bei seiner Konstruktion bietet.Aus diesem Grund ist die Änderung von
test
außerhalb des Bereichs derTestI(string)
Methode nicht sichtbar - wir haben die Referenz als Wert übergeben und jetzt hat sich dieser Wert geändert! Wenn dieString
Referenz jedoch als Referenz übergeben wurde, wird sie bei Änderung der Referenz außerhalb des Anwendungsbereichs derTestI(string)
Methode angezeigt.In diesem Fall wird entweder das Schlüsselwort ref oder out benötigt. Ich bin der Meinung, dass das
out
Keyword für diese spezielle Situation möglicherweise etwas besser geeignet ist.quelle
out
muss innerhalb der Methode zugewiesen werden, damit der Code kompiliert werden kann.ref
hat keine solche Anforderung. Auchout
Parameter werden außerhalb der Methode initialisiert - der Code in dieser Antwort ist ein Gegenbeispiel.out
Parameter können außerhalb der Methode initialisiert werden, müssen aber nicht. In diesem Fall möchten wir denout
Parameter initialisieren , um einen Punkt über die Art desstring
Typs in .NET zu demonstrieren .Tatsächlich wäre es für jedes Objekt dasselbe gewesen, dh ein Referenztyp zu sein und als Referenz zu übergeben, sind zwei verschiedene Dinge in c #.
Dies würde funktionieren, aber das gilt unabhängig vom Typ:
Auch wenn es sich bei String um einen Referenztyp handelt, handelt es sich auch um einen speziellen. Es ist so konzipiert, dass es unveränderlich ist, sodass alle Methoden die Instanz nicht ändern (sie geben eine neue zurück). Es enthält auch einige zusätzliche Dinge für die Leistung.
quelle
Hier ist eine gute Möglichkeit, über den Unterschied zwischen Werttypen, Wertübergabe, Referenztypen und Referenzübergabe nachzudenken:
Eine Variable ist ein Container.
Eine Variable vom Typ Wert enthält eine Instanz. Eine Variable vom Referenztyp enthält einen Zeiger auf eine Instanz, die an einer anderen Stelle gespeichert ist.
Durch Ändern einer Variablen vom Typ Wert wird die darin enthaltene Instanz mutiert. Durch Ändern einer Referenzvariablen wird die Instanz mutiert, auf die sie verweist.
Separate Referenztypvariablen können auf dieselbe Instanz verweisen. Daher kann dieselbe Instanz über jede Variable mutiert werden, die darauf verweist.
Ein übergebenes Wertargument ist ein neuer Container mit einer neuen Kopie des Inhalts. Ein als Referenz übergebenes Argument ist der ursprüngliche Container mit seinem ursprünglichen Inhalt.
Wenn ein Argument vom Werttyp als Wert übergeben wird: Die Neuzuweisung des Inhalts des Arguments hat keine Auswirkungen außerhalb des Gültigkeitsbereichs, da der Container eindeutig ist. Das Ändern des Arguments hat keine Auswirkungen außerhalb des Gültigkeitsbereichs, da die Instanz eine unabhängige Kopie ist.
Wenn ein Argument vom Referenztyp als Wert übergeben wird: Die Neuzuweisung des Inhalts des Arguments hat keine Auswirkungen außerhalb des Gültigkeitsbereichs, da der Container eindeutig ist. Das Ändern des Inhalts des Arguments wirkt sich auf den externen Bereich aus, da der kopierte Zeiger auf eine gemeinsam genutzte Instanz verweist.
Wenn ein Argument als Referenz übergeben wird: Die Neuzuweisung des Inhalts des Arguments wirkt sich auf den externen Bereich aus, da der Container gemeinsam genutzt wird. Das Ändern des Inhalts des Arguments wirkt sich auf den externen Bereich aus, da der Inhalt gemeinsam genutzt wird.
Abschließend:
Eine Zeichenfolgenvariable ist eine Referenzvariable. Daher enthält es einen Zeiger auf eine Instanz, die an einer anderen Stelle gespeichert ist. Bei der Übergabe als Wert wird der Zeiger kopiert. Das Ändern eines Zeichenfolgenarguments sollte sich daher auf die gemeinsam genutzte Instanz auswirken. Eine Zeichenfolgeninstanz hat jedoch keine veränderlichen Eigenschaften, sodass ein Zeichenfolgenargument ohnehin nicht geändert werden kann. Bei der Übergabe als Referenz wird der Container des Zeigers gemeinsam genutzt, sodass die Neuzuweisung weiterhin Auswirkungen auf den externen Bereich hat.
quelle
" Ein Bild sagt mehr als tausend Worte ".
Ich habe hier ein einfaches Beispiel, es ähnelt Ihrem Fall.
Das ist, was passiert ist:
s1
unds2
Variablen verweisen auf dasselbe"abc"
Zeichenfolgenobjekt."abc"
ändert sich das Zeichenfolgenobjekt nicht selbst (zu"def"
), sondern es"def"
wird stattdessen ein neues Zeichenfolgenobjekt erstellt, auf das danns1
verwiesen wird.s2
Verweist immer noch auf ein"abc"
Zeichenfolgenobjekt, das ist also die Ausgabe.quelle
Die obigen Antworten sind hilfreich. Ich möchte nur ein Beispiel hinzufügen, das meiner Meinung nach deutlich zeigt, was passiert, wenn wir Parameter ohne das Schlüsselwort ref übergeben, auch wenn dieser Parameter ein Referenztyp ist:
quelle
Für neugierige Köpfe und um das Gespräch zu vervollständigen: Ja, String ist ein Referenztyp :
Beachten Sie jedoch, dass diese Änderung nur in einem unsicheren Block funktioniert ! weil Strings unveränderlich sind (von MSDN):
Und denken Sie daran:
quelle
Ich glaube, Ihr Code ist analog zu dem folgenden, und Sie hätten nicht erwarten dürfen, dass sich der Wert aus demselben Grund geändert hat, aus dem er hier nicht vorhanden wäre:
quelle
Versuchen:
quelle