2010-12-03 27 views
44

Ich bekomme immer wieder das gleiche Problem, wo ein Objekt, das ich verweisen möchte, kopiert wird oder wo ein Objekt, das ich kopieren möchte, verwiesen wird. Dies passiert, wenn ich den Operator = verwende.Wann wird ein C# -Wert/Objekt kopiert und wann wird seine Referenz kopiert?

Zum Beispiel, wenn ich das Objekt in einer anderen Form sende, das heißt:

SomeForm myForm = new SomeForm(); 
SomeObject myObject = new SomeObject(); 
myForm.formObject = myObject; 

... und ändern Sie dann das Objekt in der Form, wird das ursprüngliche Objekt nicht geändert bekommen. Es ist, als ob das Objekt kopiert und nicht referenziert wurde. Doch wenn ich dies tun:

SomeObject myObject = new SomeObject(); 
SomeObject anotherObject = new SomeObject(); 
anotherObject = myObject; 

... und dann ändern anotherObject, myObject ebenfalls geändert wird.

Der erschwerende Fall ist, wenn ich versuche, eine meiner definierten Objekte zu klonen:

dieses
public class SomeObject 
{ 
    double value1, value2; 

    //default constructor here 

    public SomeObject(val1, val2) 
    { 
     value1 = val1; 
     value2 = val2; 
    } 

    public void Clone(SomeObject thingToCopy) 
    { 
     this.value1 = thingToCopy.value1; 
     this.value2 = thingToCopy.value2; 
    } 
} 

wenn ich ...

SomeObject obj1 = new SomeObject(1, 2); 
SomeObject obj2 = new SomeObject(); 
obj2.Clone(obj1); 

... obj1 verwiesen wird, und alle Änderungen zu obj2 Änderungen obj1.

Systemobjekte wie int, double, string usw. scheinen immer kopiert zu werden, außer im Falle der obigen Klonmethode.

Meine Frage ist, nicht die Verwendung des Schlüsselwortes ref in Funktionen zu berücksichtigen, wann ein Objekt kopiert wird und wann ein Objekt in jedem Fall referenziert wird (dh bei der Übergabe an Funktionen, beim Setzen als andere Objekte (wie die ersten beiden obigen Beispiele), beim Kopieren von Elementvariablen wie das dritte Beispiel usw.)?

Antwort

32

Es ist schwierig, diese Art von Frage genau zu beantworten, ohne eine Menge Zeit damit zu verschwenden, Ihre Worte sorgfältig auszuwählen.

Ich habe getan, so in ein paar Artikel, die Sie nützlich finden können:

Das ist nicht, zu sagen, dass die Artikel perfekt sind, natürlich - weit gefehlt - aber ich habe versucht, so klar wie möglich zu sein.

Ich denke, eine wichtige Sache ist, die beiden Konzepte (Parameterübergabe und Referenz vs Werttypen) in Ihrem Kopf zu trennen.

an Ihren spezifischen Beispielen anschauen:

SomeForm myForm = new SomeForm(); 
SomeObject myObject = new SomeObject(); 
myForm.formObject = myObject; 

Das bedeutet, dass myForm.formObject und myObject auf derselben Instanz von SomeObject beziehen sie - wie zwei Personen mit getrenntem Stück Papier, mit jedem der gleichen Adresse geschrieben hat Sie. Wenn du auf ein Stück Papier zur Adresse gehst und das Haus rot maltest, dann gehe zu der Adresse auf dem zweiten Blatt Papier, du siehst ein rotes Haus.

Es ist nicht klar, was Sie mit "und dann ändern Sie das Objekt in der Form", weil der Typ, den Sie bereitgestellt haben, unveränderlich ist. Es gibt keine Möglichkeit, das Objekt selbst zu verändern. Sie können myForm.formObject ändern, um auf eine andere Instanz von SomeObject zu verweisen, aber das ist, als würde man die Adresse auf ein Blatt Papier schreiben und stattdessen eine andere Adresse schreiben. Das wird nicht ändern, was auf dem anderen Blatt geschrieben ist.

Wenn Sie einen kurzen liefern könnte aber komplette Programm, dessen Verhalten Sie nicht verstehen (im Idealfall eine Konsolenanwendung, nur Dinge zu halten kürzer und einfacher) es wäre einfacher, über die Dinge konkret zu sprechen.

+0

Ich werde einen schreiben müssen . Ich verstehe Zeiger größtenteils aus C++, ich verstehe nur nicht, wenn sie in Bezug auf Werte und Referenztypen in C# verwendet werden, da es keine explizite "sichere" Zeigerverwendung/Operatoren in C# gibt. –

+1

Ich werde das Programm später schreiben als jetzt bin ich bei der Arbeit. Danke für die Hilfe. Ich werde die Artikel auch lesen. –

+3

Die Analogie der Adresse auf separaten Zetteln ist hilfreich, um das Konzept der Referenztypen zu verstehen. – Martin

4

Hallo Mike Alle Objekte, die von ValueType abgeleitet sind, wie z. B. Struktur oder andere primitive Typen sind Werttypen. Das bedeutet, dass sie kopiert werden, wenn Sie sie einer Variablen zuweisen oder als Methodenparameter übergeben. Andere Typen sind Referenztypen, dh wenn Sie einer Variablen einen Referenztyp zuweisen, wird der Wert nicht zugewiesen, sondern die Adresse im Speicherbereich wird der Variablen zugewiesen. Sie sollten auch beachten, dass Sie einen Werttyp als Referenz übergeben können, indem Sie ref Schlüsselwort verwenden. Hier ist die Syntax

public void MyMethod(ref int a) { a = 25 } 
int i = 20; 
MyMethod(ref i); //Now i get's updated to 25. 

Hoffe, es hilft :)

+0

Entschuldigung, hier zu nitpick. Objekte werden nicht abgeleitet. Typen tun. Objekt ist im Allgemeinen der Begriff, der verwendet wird, um eine Instanz eines Typs zu beschreiben. –

+0

Sie können ein Argument * durch * Referenz mit 'ref' übergeben, aber das ist nicht dasselbe wie * als * eine Referenz zu übergeben. Ich denke, dass es sich lohnt, zwischen den beiden zu unterscheiden. –

+0

@JonSkeet: Ich weiß, dass dies eine alte Frage ist, aber können Sie einige Informationen über Ihren letzten Kommentar klären oder teilen? Ich meine, passiere _by_ reference vs passing _als_ reference? Vielen Dank im Voraus :) Ich frage, da ich immer dachte, dass beide mit dem gleichen Ergebnis enden werden, das den übergebenen Objektwert beeinflusst, wenn es innerhalb einer externen Methode übergeben wird. –

1

Mit Bezug auf Ihre Objekte klonen, wenn die Werte Sie kopieren von einem Objekt zum anderen Referenz sind Typen dann jede Änderung dieser Werte in das ursprüngliche Objekt wird Auswirkungen auf die Werte im kopierten Objekt (da sie nur Verweise auf das gleiche Objekt sind)

Wenn Sie ein Objekt klonen müssen, das Eigenschaften hat, die Referenztypen sind, müssen Sie diese Typen klonbar machen oder manuell kopieren sie durch Instanziieren neuer Instanzen nach Bedarf.

Erwägen Sie die Verwendung der IClonable Schnittstelle, obwohl es nicht die beste Lösung imho ist.