Betrachten Sie diesen Code:
public class Program
{
private static void Main(string[] args)
{
var person1 = new Person { Name = "Test" };
Console.WriteLine(person1.Name);
Person person2 = person1;
person2.Name = "Shahrooz";
Console.WriteLine(person1.Name); //Output: Shahrooz
person2 = null;
Console.WriteLine(person1.Name); //Output: Shahrooz
}
}
public class Person
{
public string Name { get; set; }
}
Natürlich, wenn die Zuordnung person1
zu person2
und die Name
Eigenschaft person2
geändert wird , die Name
von person1
ebenfalls wird geändert. person1
und person2
haben die gleiche Referenz.
Warum ist person2 = null
die person1
Variable dann auch nicht null?
person1
undperson2
gibt 2 verschiedene Visitenkarten , die eine Adresse enthalten. Wenn Sie das tun ,person1 = person2
dannperson1
ist jetzt eine Kopieperson2
. Es handelt sich immer noch um unterschiedliche Visitenkarten, aber beide enthalten dieselbe Adresse, die auf dasselbe Objekt zeigt. Das Objekt selbst ändert sich nicht.person2
im zweiten Bild entfernen, weil er auf nichts verweist - es ist keine baumelnde Referenz.Betrachten Sie
person1
undperson2
als Zeiger auf einen Speicherort. Im ersten Schritt wird nurperson1
die Adresse des Objekts aus dem Speicher und späterperson2
die Adresse des Speicherorts des Objekts aus dem Speicher gespeichert. Wenn Sie später zuordnennull
zuperson2
,person1
bleibt hiervon unberührt. Deshalb sehen Sie das Ergebnis.Sie können lesen: Wert vs Referenztypen von Joseph Albahari
Ich werde versuchen, dasselbe Konzept anhand des folgenden Diagramms darzustellen.
Erstellt ein neues Objekt vom Typ Person und die
person1
Referenz (Zeiger) zeigt auf den Speicherort im Speicher.Erstellt eine neue Referenz (Zeiger)
person2
, die im Speicher auf dieselbe verweist.Die Objekteigenschaft Name wurde in einen neuen Wert geändert,
person2
da beide Referenzen auf dasselbe Objekt verweisen undConsole.WriteLine(person1.Name);
ausgegeben werdenShahrooz
.Nach dem Zuweisen
null
zurperson2
Referenz zeigt es auf nichts, hält aberperson1
immer noch die Referenz auf das Objekt.(Schließlich sollten Sie für die Speicherverwaltung sehen, dass der Stapel ein Implementierungsdetail ist, Teil 1, und der Stapel ist ein Implementierungsdetail, Teil 2 von Eric Lippert.)
quelle
person
auf dem Heap und seinen Referenzen gespeichertperson1
undperson2
auf dem Stapel wäre. Aber ich stimme zu, dass es eher ein Implementierungsdetail ist. Aber Stapel und Haufen in der Antwort zu haben (wie Albaharis Artikel) würde es IMO klarer machen.Sie haben die
person2
Referenz geändert , verweisen dortnull
jedochperson1
nicht darauf.Was ich meine ist, dass wenn wir uns die Aufgabe ansehen
person2
undperson1
vor ihr, dann beide auf dasselbe Objekt verweisen. Dann weisen Sie zuperson2 = null
, sodass Person 2 jetzt auf einen anderen Typ verweist. Das Objekt, aufperson2
das verwiesen wurde , wurde nicht gelöscht .Ich habe dieses GIF erstellt, um es zu veranschaulichen:
quelle
Weil Sie den Verweis auf gesetzt haben
null
.Wenn Sie eine Referenz festlegen
null
, ist die Referenz selbstnull
.. nicht das Objekt, auf das sie verweist.Stellen Sie sich diese als eine Variable vor, die einen Versatz von 0 enthält. Sie
person
hat den Wert 120. Sieperson2
hat den Wert 120. Die Daten am Versatz 120 sind dasPerson
Objekt. Wenn Sie dies tun:person2 = null;
..sie sagen effektiv ,
person2 = 0;
. Hat aberperson
noch den Wert 120.quelle
copy-by-value
Semantik ab. Sie kopieren den Wert der Referenz.Beide
person
undperson2
zeigen auf dasselbe Objekt. Wenn Sie also den Namen eines der beiden ändern, werden beide geändert (da sie auf dieselbe Struktur im Speicher verweisen).Aber wenn Sie die Einstellung
person2
aufnull
, machen Sieperson2
in einen Null - Zeiger, so dass ist nicht darauf auf das gleiche Objekt wieperson
mehr. Es wird nichts mit dem Objekt selbst tun, um es zu zerstören, und da esperson
immer noch auf das Objekt verweist, wird es auch nicht durch die Speicherbereinigung getötet.Wenn Sie auch festlegen
person = null
und keine anderen Verweise auf das Objekt haben, wird es schließlich vom Garbage Collector entfernt.quelle
person1
undperson2
auf dieselbe Speicheradresse zeigen. Wenn Sie null setzen, nullenperson2
Sie die Referenz und nicht die Speicheradresse. Wenn Sie alsoperson1
weiter auf diese Speicheradresse verweisen, ist dies der Grund. Wenn Sie dasClasss Person
in ein ändern, ändert sichStruct
das Verhalten.quelle
Ich finde es am hilfreichsten, sich Referenztypen als Objekt-IDs vorzustellen. Wenn eine Variable vom Klassentyp vorhanden ist
Car
,myCar = new Car();
fordert die Anweisung das System auf, ein neues Auto zu erstellen und seine ID zu melden (sagen wir, es ist Objekt Nr. 57). es setzt dann "Objekt # 57" in die VariablemyCar
. Wenn man schreibtCar2 = myCar;
, schreibt das "Objekt # 57" in die Variable Car2. Wenn man schreibtcar2.Color = blue;
, weist dies das System an, das durch Car2 identifizierte Auto (z. B. Objekt Nr. 57) zu finden und es blau zu lackieren.Die einzigen Operationen, die direkt an Objekt-IDs ausgeführt werden, sind das Erstellen eines neuen Objekts und das Abrufen der ID, das Abrufen einer "leeren" ID (dh null), das Kopieren einer Objekt-ID in eine Variable oder einen Speicherort, der sie enthalten kann, und das Überprüfen, ob zwei Objekt-IDs stimmen überein (beziehen sich auf dasselbe Objekt). Alle anderen Anforderungen fordern das System auf, das Objekt zu finden, auf das sich eine ID bezieht, und auf dieses Objekt zu reagieren (ohne die Variable oder andere Entität zu beeinflussen, die die ID enthielt).
In vorhandenen Implementierungen von .NET enthalten Objektvariablen wahrscheinlich Zeiger auf Objekte, die auf einem durch Müll gesammelten Heap gespeichert sind. Dies ist jedoch ein nicht hilfreiches Implementierungsdetail, da zwischen einer Objektreferenz und anderen Zeigern ein kritischer Unterschied besteht. Es wird allgemein angenommen, dass ein Zeiger den Ort von etwas darstellt, das lange genug stehen bleibt, um bearbeitet zu werden. Objektreferenzen nicht. Ein Codeteil kann das SI-Register mit einem Verweis auf ein Objekt unter der Adresse 0x12345678 laden, es verwenden und dann unterbrochen werden, während der Garbage Collector das Objekt an die Adresse 0x23456789 verschiebt. Das würde sich wie eine Katastrophe anhören, aber der Müll wird die mit dem Code verknüpften Metadaten untersuchen und feststellen, dass der Code SI verwendet hat, um die Adresse des verwendeten Objekts zu speichern (dh 0x12345678). Stellen Sie fest, dass das Objekt mit 0x12345678 in 0x23456789 verschoben wurde, und aktualisieren Sie SI so, dass es 0x23456789 enthält, bevor es zurückgegeben wird. Beachten Sie, dass in diesem Szenario der in SI gespeicherte numerische Wert vom Garbage Collector geändert wurde, auf den jedoch verwiesen wurdedas gleiche Objekt vor dem Umzug und danach. Wenn vor dem Verschieben auf das 23.592ste Objekt verwiesen wurde, das seit dem Start des Programms erstellt wurde, wird dies danach fortgesetzt. Interessanterweise speichert .NET für die meisten Objekte keine eindeutige und unveränderliche Kennung. Bei zwei Snapshots des Arbeitsspeichers eines Programms kann nicht immer festgestellt werden, ob ein bestimmtes Objekt im ersten Snapshot im zweiten vorhanden ist oder ob alle Spuren davon aufgegeben und ein neues Objekt erstellt wurde, das zufällig so aussieht alle beobachtbaren Details.
quelle
person1 und person2 sind zwei separate Referenzen auf dem Stapel, die auf dasselbe Person-Objekt auf dem Heap verweisen.
Wenn Sie eine der Referenzen löschen, wird sie aus dem Stapel entfernt und zeigt nicht mehr auf das Personenobjekt auf dem Heap. Die andere Referenz bleibt erhalten und verweist weiterhin auf das vorhandene Personenobjekt auf dem Heap.
Sobald alle Verweise auf das Person-Objekt entfernt wurden, entfernt der Garbage Collector das Objekt schließlich aus dem Speicher.
quelle
Wenn Sie einen Referenztyp erstellen, wird tatsächlich eine Referenz kopiert, wobei alle Objekte auf denselben Speicherort verweisen. Wenn Sie jedoch Person2 = Null zugewiesen haben, hat dies keine Auswirkung, da person2 nur eine Kopie der Referenzperson ist und wir gerade eine Kopie gelöscht haben der Referenz .
quelle
Beachten Sie, dass Sie eine Wertsemantik erhalten können, indem Sie zu a wechseln
struct
.public class Program { static void Main() { var person1 = new Person { Name = "Test" }; Console.WriteLine(person1.Name); Person person2 = person1; person2.Name = "Shahrooz"; Console.WriteLine(person1.Name);//Output:Test Console.WriteLine(person2.Name);//Output:Shahrooz person2 = new Person{Name = "Test2"}; Console.WriteLine(person2.Name);//Output:Test2 } } public struct Person { public string Name { get; set; } }
quelle
public class Program { private static void Main(string[] args) { var person = new Person {Name = "Test"}; Console.WriteLine(person.Name); Person person2 = person; person2.Name = "Shahrooz"; Console.WriteLine(person.Name);//Output:Shahrooz // Here you are just erasing a copy to reference not the object created. // Single memory allocation in case of reference type and parameter // are passed as a copy of reference type . person2 = null; Console.WriteLine(person.Name);//Output:Shahrooz } } public class Person { public string Name { get; set; } }
quelle
Sie kopieren zuerst den Verweis
person1
aufperson2
. Jetztperson1
undperson2
beziehen sich auf das gleiche Objekt, die Modifikationen auf den Wert des Objekts bedeutet ( das heißt die Änderung derName
Eigenschaft) kann unter beiden Variablen zu beobachten. Wenn Sie dann null zuweisen, entfernen Sie die Referenz, der Sie gerade zugewiesen habenperson2
. Es ist nurperson1
jetzt zugeordnet. Beachten Sie, dass die Referenz selbst nicht geändert wird.Wenn Sie eine Funktion hätten, die ein Argument als Referenz akzeptiert, könnten Sie als
reference to person1
Referenz übergeben und die Referenz selbst ändern:public class Program { private static void Main(string[] args) { var person1 = new Person { Name = "Test" }; Console.WriteLine(person1.Name); PersonNullifier.NullifyPerson(ref person1); Console.WriteLine(person1); //Output: null } } class PersonNullifier { public static void NullifyPerson( ref Person p ) { p = null; } } class Person { public string Name{get;set;} }
quelle
Sprichwort:
person2.Name = "Shahrooz";
folgt der Referenz
person2
und "mutiert" (ändert) das Objekt, zu dem die Referenz führt. Die Referenz selbst (der Wert vonperson2
) ist unverändert; Wir verweisen immer noch auf dieselbe Instanz unter derselben "Adresse".Zuweisen zu
person2
wie in:person2 = null;
ändert die Referenz . Es wird kein Objekt geändert. In diesem Fall wird der Referenzpfeil von einem Objekt zu "nichts" "verschoben"
null
. Eine Zuordnung wieperson2 = new Person();
würde aber auch nur die Referenz ändern. Kein Objekt ist mutiert.quelle