Der string
-Typ in C # ist ein Referenztyp, und wenn ein Referenzargument nach Wert übergeben wird, wird der Verweis kopiert, sodass ich den ref
-Modifikator nicht verwenden muss. Allerdings muss ich den Modifizierer ref
verwenden, um die Eingabe string
zu ändern. Warum ist das?
Der Grund dafür, dass Sie den String-Parameter referenzieren müssen, besteht darin, dass Sie, obwohl Sie einen Verweis auf ein String-Objekt übergeben, nur den aktuell im Parameter variablen gespeicherten Verweis ersetzen . Mit anderen Worten, Sie haben geändert, worauf der Parameter verweist, aber das ursprüngliche Objekt ist unverändert.
Wenn Sie den Parameter referenzieren, haben Sie der Funktion gesagt, dass der Parameter tatsächlich ein Alias für die übergebene Variable ist, so dass die Zuweisung den gewünschten Effekt hat.
BEARBEITEN : Beachten Sie, dass while eine unveränderliche Referenz ist, die hier nicht relevant ist. Da Sie nur versuchen, ein neues Objekt zuzuordnen (in diesem Fall das Zeichenfolgenobjekt "modifiziert"), funktioniert Ihr Ansatz nicht mit dem Referenztyp any . Betrachten Sie zum Beispiel diese geringfügige Änderung an Ihrem Code:
%Vor%Nun ist der Array-Test fehlgeschlagen, weil Sie der non-ref-Parametervariablen ein neues Objekt zuweisen.
Es hilft, string
mit einem Typ zu vergleichen, der wie string
ist, aber veränderbar ist. Lassen Sie uns ein kurzes Beispiel mit StringBuilder
sehen:
Dies erzeugt:
%Vor% Wir sehen also, dass es für einen veränderbaren Typ, dh einen Typ, dessen Wert geändert werden kann, möglich ist, einen Verweis auf diesen Typ an eine Methode wie ChangeBuilder
zu übergeben und nicht ref
oder out
und Nach dem Aufruf haben wir den Wert noch geändert.
Und beachten Sie, dass wir zu keinem Zeitpunkt builder
auf einen anderen Wert in ChangeBuilder
gesetzt haben.
Im Gegensatz dazu, wenn wir dasselbe mit string machen:
%Vor%Dies erzeugt:
%Vor% Warum? Weil wir in TryToChangeString
nicht wirklich den Inhalt der Zeichenfolge ändern, auf die die Variable s
verweist, ersetzen wir s
durch eine völlig neue Zeichenfolge. Außerdem ist s
eine lokale Variable für TryToChangeString
und ersetzt somit den Wert von s
innerhalb die Funktion hat keine Auswirkung auf die Variable, die an den Funktionsaufruf übergeben wurde.
Da ein string
unveränderlich ist, gibt es keine Möglichkeit, die Anruferzeichenfolge zu beeinflussen, wenn Sie ref
oder out
nicht verwenden.
Letztendlich macht das letzte Beispiel was wir wollen mit string
:
Dies erzeugt:
%Vor% Der ref
-Parameter macht tatsächlich die zwei s
-Variablen Aliase füreinander. Es ist, als wären sie die gleiche Variable .
Strings sind unveränderlich - Sie ändern die Zeichenfolge nicht, sondern ersetzen das Objekt durch den Referenzpunkt durch einen anderen.
Vergleichen Sie das mit z. B. einer Liste: Um Artikel hinzuzufügen, brauchen Sie nicht ref. Um die gesamte Liste durch ein anderes Objekt zu ersetzen, benötigen Sie ref (oder out).
Dies ist der Fall für alle unveränderlichen Typen. string
ist zufällig unveränderlich.
Um den unveränderlichen Typ außerhalb der Methode zu ändern, müssen Sie die Referenz ändern. Daher muss entweder ref
oder out
außerhalb der Methode wirksam sein.
Hinweis: Es ist erwähnenswert, dass Sie in Ihrem Beispiel einen bestimmten Fall aufrufen, der nicht mit dem anderen Beispiel übereinstimmt: Sie verweisen tatsächlich auf eine andere Referenz, anstatt einfach die vorhandene Referenz zu ändern. Wie von dlev (und dem Skeet selbst in meinen Kommentaren) angemerkt, wenn Sie das Gleiche für alle anderen Typen (zB val = new int[1]
), einschließlich veränderbarer, getan haben, werden Sie Ihre Änderungen "verlieren" Sobald die Methode zurückkehrt, weil sie nicht mit demselben Objekt im Speicher passiert ist, außer Sie verwenden ref
oder out
wie Sie es mit string
oben getan haben.
Um hoffentlich zu verdeutlichen:
Sie übergeben einen Zeiger, der auf Ihr Objekt im Speicher zeigt. Ohne ref
oder out
wird ein neuer Zeiger erzeugt, der genau auf die gleiche Stelle zeigt, und alle Änderungen erfolgen mit dem kopierten Zeiger. Mit ihnen wird derselbe Zeiger verwendet und alle Änderungen am Zeiger werden außerhalb der Methode angezeigt.
Wenn Ihr Objekt änderbar ist, bedeutet dies, dass es geändert werden kann, ohne eine neue Instanz des Objekts zu erstellen. Wenn Sie eine neue Instanz erstellen, müssen Sie auf eine andere Stelle im Speicher verweisen, was bedeutet, dass Sie den Zeiger ändern müssen.
Wenn Ihr Objekt jetzt unveränderlich ist, bedeutet dies, dass es nicht geändert werden kann, ohne eine neue Instanz zu erstellen.
In Ihrem Beispiel haben Sie eine neue Instanz von string
(gleich "modified"
) erstellt und dann den Zeiger ( input
) so geändert, dass er auf diese neue Instanz verweist. Für das Array int
haben Sie einen der 10 Werte geändert, auf die effektiv val
zeigt, und die nicht mit dem Zeiger von val
verwechselt werden muss - es geht einfach dorthin, wo Sie wollen (das erste Element des Arrays) ) und modifiziert dann diesen ersten Wert in-place.
Ein ähnlicheres Beispiel wäre (gestohlen von dlev, aber das ist, wie man sie wirklich vergleichbar macht):
%Vor% Beide Funktionen ändern den Zeiger ihrer Parameter. Nur weil Sie ref
verwendet haben, "erinnert" sich input
an seine Änderungen, denn wenn der Zeiger geändert wird, ändert er den übergebenen Zeiger und nicht nur eine Kopie davon.
val
ist immer noch ein Array von 10 int
s außerhalb der Funktion, und val[0]
ist immer noch 1, weil " val
" in Function2
ein anderes Zeiger, der ursprünglich auf den gleichen Ort wie% val
von Main verweist, aber nach dem Erstellen des neuen Arrays auf eine andere Stelle zeigt (der andere Zeiger zeigt auf das neue Array und der ursprüngliche Zeiger zeigt weiter zum selben Ort).
Wenn ich ref
mit dem Array int
verwendet hätte, dann hätte sich das geändert. Und es hätte sich auch in der Größe verändert.
Die Verwirrung besteht darin, dass Ref-Typreferenzen standardmäßig als Wert übergeben werden. Um die Referenz selbst (auf die das Objekt verweist) zu ändern, müssen Sie die Referenz als Referenz übergeben - mit ref
.
In Ihrem Fall behandeln Sie Strings - wenn Sie einer Variablen einen String zuweisen (oder anhängen usw.), wird der Verweis geändert, da Strings unveränderlich sind. Es gibt auch keine Möglichkeit, dies zu vermeiden. Verwenden Sie daher ref
.
Sie haben Recht. Arrays und Zeichenfolgen sind Referenztypen. Aber um ehrlich zu sein und ähnliches Verhalten zu vergleichen, sollte man so etwas schreiben:
%Vor% In Ihrem Beispiel führen Sie jedoch eine Schreiboperation in einem Element eines eindimensionalen C # -Arrays über den Verweis val
durch.