Wenn ich a string
an eine Funktion übergebe, wird ein Zeiger auf den Inhalt der Zeichenfolge übergeben, oder wird die gesamte Zeichenfolge wie a an die Funktion auf dem Stapel übergeben struct
?
Wenn ich a string
an eine Funktion übergebe, wird ein Zeiger auf den Inhalt der Zeichenfolge übergeben, oder wird die gesamte Zeichenfolge wie a an die Funktion auf dem Stapel übergeben struct
?
Antworten:
Eine Referenz wird übergeben; Es ist jedoch technisch nicht als Referenz übergeben. Dies ist eine subtile, aber sehr wichtige Unterscheidung. Betrachten Sie den folgenden Code:
void DoSomething(string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomething(strMain);
Console.WriteLine(strMain); // What gets printed?
}
Es gibt drei Dinge, die Sie wissen müssen, um zu verstehen, was hier passiert:
strMain
werden sie nicht als Referenz übergeben. Es ist ein Referenztyp, aber die Referenz selbst wird als Wert übergeben . Jedes Mal, wenn Sie einen Parameter ohne das ref
Schlüsselwort übergeben (ohne out
Parameter), haben Sie etwas nach Wert übergeben.Das muss also bedeuten, dass Sie ... eine Referenz nach Wert übergeben. Da es sich um einen Referenztyp handelt, wurde nur die Referenz auf den Stapel kopiert. Aber was heißt das?
C # -Variablen sind entweder Referenztypen oder Werttypen . C # -Parameter werden entweder als Referenz oder als Wert übergeben . Terminologie ist hier ein Problem; diese klingen wie das gleiche, sind es aber nicht.
Wenn Sie einen Parameter vom Typ ANY übergeben und das ref
Schlüsselwort nicht verwenden , haben Sie es als Wert übergeben. Wenn Sie es als Wert übergeben haben, haben Sie wirklich eine Kopie übergeben. Wenn der Parameter jedoch ein Referenztyp war, war das, was Sie kopiert haben, die Referenz, nicht das, worauf sie zeigte.
Hier ist die erste Zeile der Main
Methode:
string strMain = "main";
In dieser Zeile haben wir zwei Dinge erstellt: eine Zeichenfolge, deren Wert main
irgendwo im Speicher gespeichert ist, und eine Referenzvariable, die strMain
darauf verweist.
DoSomething(strMain);
Nun geben wir diesen Hinweis an weiter DoSomething
. Wir haben es als Wert übergeben, das heißt, wir haben eine Kopie erstellt. Es ist ein Referenztyp, das heißt, wir haben die Referenz kopiert, nicht die Zeichenfolge selbst. Jetzt haben wir zwei Referenzen, die jeweils auf denselben Wert im Speicher verweisen.
Hier ist die Spitze der DoSomething
Methode:
void DoSomething(string strLocal)
Kein ref
Schlüsselwort, so strLocal
und strMain
sind zwei verschiedene Verweise auf den gleichen Wert zeigen. Wenn wir neu zuweisen strLocal
...
strLocal = "local";
... wir haben den gespeicherten Wert nicht geändert; Wir haben die aufgerufene Referenz genommen strLocal
und sie auf eine brandneue Saite gerichtet. Was passiert, strMain
wenn wir das tun? Nichts. Es zeigt immer noch auf die alte Saite.
string strMain = "main"; // Store a string, create a reference to it
DoSomething(strMain); // Reference gets copied, copy gets re-pointed
Console.WriteLine(strMain); // The original string is still "main"
Lassen Sie uns das Szenario für eine Sekunde ändern. Stellen Sie sich vor, wir arbeiten nicht mit Zeichenfolgen, sondern mit einem veränderlichen Referenztyp, wie einer von Ihnen erstellten Klasse.
class MutableThing
{
public int ChangeMe { get; set; }
}
Wenn Sie dem Verweis auf das Objekt folgen,objLocal
auf das es zeigt, können Sie seine Eigenschaften ändern:
void DoSomething(MutableThing objLocal)
{
objLocal.ChangeMe = 0;
}
Es ist immer noch nur eine MutableThing
im Speicher, und sowohl die kopierte Referenz als auch die ursprüngliche Referenz zeigen immer noch darauf. Die Eigenschaften des MutableThing
selbst haben sich geändert :
void Main()
{
var objMain = new MutableThing();
objMain.ChangeMe = 5;
Console.WriteLine(objMain.ChangeMe); // it's 5 on objMain
DoSomething(objMain); // now it's 0 on objLocal
Console.WriteLine(objMain.ChangeMe); // it's also 0 on objMain
}
Ah, aber Saiten sind unveränderlich! Es ist keine ChangeMe
Eigenschaft festzulegen. strLocal[3] = 'H'
In C # können Sie dies nicht wie mit einem char
Array im C-Stil tun . Sie müssen stattdessen eine ganz neue Zeichenfolge erstellen. Die einzige Möglichkeit zum Ändern strLocal
besteht darin, die Referenz auf eine andere Zeichenfolge zu verweisen. Dies bedeutet, dass nichts, was Sie tun, strLocal
Auswirkungen haben kann strMain
. Der Wert ist unveränderlich und die Referenz ist eine Kopie.
Um zu beweisen, dass es einen Unterschied gibt, geschieht Folgendes, wenn Sie eine Referenz als Referenz übergeben:
void DoSomethingByReference(ref string strLocal)
{
strLocal = "local";
}
void Main()
{
string strMain = "main";
DoSomethingByReference(ref strMain);
Console.WriteLine(strMain); // Prints "local"
}
Dieses Mal wird die Zeichenfolge in Main
wirklich geändert, da Sie die Referenz übergeben haben, ohne sie auf den Stapel zu kopieren.
Obwohl Zeichenfolgen Referenztypen sind, bedeutet die Übergabe als Wert, dass alles, was im Angerufenen vor sich geht, die Zeichenfolge im Aufrufer nicht beeinflusst. Da es sich jedoch um Referenztypen handelt, müssen Sie nicht die gesamte Zeichenfolge in den Speicher kopieren, wenn Sie sie weitergeben möchten.
ref
Schlüsselwort erforderlich ist . Um zu beweisen, dass das Übergeben von Referenzen einen Unterschied macht, sehen Sie sich diese Demo an: rextester.com/WKBG5978
ref
Schlüsselwort nützlich ist. Ich habe nur versucht zu erklären, warum man daran denken könnte , einen Referenztyp in C # als Wert zu übergeben. Dies scheint der "traditionelle" (dh C) Begriff zu sein, Referenz zu übergeben (und einen Referenztyp zu übergeben) als Referenz in C # scheint eher so zu sein, als würde eine Referenz auf eine Referenz nach Wert übergeben).
Foo(string bar)
gedacht werden könnte . Sicher, so sollte man es nicht jeden Tag denken, aber es hat mir tatsächlich geholfen, endlich zu verstehen, was unter der Haube passiert. Foo(char* bar)
Foo(ref string bar)
Foo(char** bar)
Foo(char*& bar)
Foo(string& bar)
Zeichenfolgen in C # sind unveränderliche Referenzobjekte. Dies bedeutet, dass Verweise auf sie (nach Wert) weitergegeben werden. Sobald eine Zeichenfolge erstellt wurde, können Sie sie nicht mehr ändern. Methoden , die Versionen des Strings (Strings, getrimmte Versionen, etc.) erstellen modifizierten modifizierte produzieren Kopien der Original - Zeichenkette.
Saiten sind Sonderfälle. Jede Instanz ist unveränderlich. Wenn Sie den Wert einer Zeichenfolge ändern, weisen Sie eine neue Zeichenfolge im Speicher zu.
Es wird also nur die Referenz an Ihre Funktion übergeben, aber wenn die Zeichenfolge bearbeitet wird, wird sie zu einer neuen Instanz und ändert die alte Instanz nicht.
Uri
(Klasse) und Guid
(Struktur) auch Sonderfälle. Ich sehe nicht mehr System.String
, wie sich ein "Werttyp" verhält als andere unveränderliche Typen ... entweder von Klassen- oder Strukturursprüngen.
Uri
& Guid
- können Sie einer Stringvariablen einfach einen String-Literal-Wert zuweisen. Die Zeichenfolge scheint veränderbar zu sein, wie eine int
Neuzuweisung, aber sie erstellt implizit ein Objekt - kein new
Schlüsselwort.