2010-05-26 7 views
5

Könnten Sie bitte das folgende Verhalten der C# -Klasse erklären. Ich erwarte das classResult als "Class Lijo"; aber der tatsächliche Wert ist "geändert".lPass nach Wert & Pass by Referenz

Wir machen eine Kopie der Referenz. Obwohl die Kopie auf dieselbe Adresse verweist, kann die Methode, die das Argument erhält, das Original nicht ändern.

Noch warum der Wert geändert wird?

public partial class _Default : Page 
{ 
    protected void Page_Load(object sender, EventArgs e) 
    { 
     String nameString = "string Lijo"; 

     Person p = new Person(); 
     p.Name = "Class Lijo"; 

     Utilityclass.TestMethod(nameString, p); 
     string classResult = p.Name; 
     Response.Write(nameString + "....." + classResult); 
    } 
} 

public class Utilityclass 
{ 
    public static void TestMethod(String nameString, Person k) 
    { 
     nameString = "Changed"; 
     k.Name = "Changed"; 
    } 
} 

public class Person 
{ 
    public string Name 
    { 
     get; set; 
    } 
} 

aktualisieren: Wenn ich einen String übergeben, ist es nicht wirklich geändert hat bekommen.

+3

Darf ich vorschlagen, dass ich [Mr. Skeets ausgezeichneten Artikel zu diesem Thema] gelesen habe (http://www.yoda.arachsys.com/csharp/parameters.html). – Alconja

+0

Obwohl String ein Objekt ist, ist es unveränderlich. Wenn Sie also eine Zeichenfolge übergeben, wird die ursprüngliche Referenz nicht geändert. – trendl

+0

Ich hatte dies gelesen [Artikel] (http://zuta-developer.blogspot.com/2012/06/net- parameter-passing.html) Dieser Artikel zeigt Ihnen, wie die Parameterübergabe im Speicher gehandhabt wird. – Developex

Antwort

22

Die kürzeste Antwort ist (Bonuspunkte, wenn Sie diese mit ints ausprobieren!): Lesen my article on parameter passing was geht dazu in ziemlich viel Detail.

Die etwas längere Antwort ist, diese beiden Methoden zu vergleichen, von denen beide Wert Parameter verwenden:

public void ChangeMe(string x) 
{ 
    x = "changed"; 
} 

public void ChangeMe(Person x) 
{ 
    x.Name = "changed"; 
} 

Im ersten Fall, ändern Sie den Wert des Parameters. Das ist völlig isoliert von dem ursprünglichen Argument. Sie können den Inhalt der Zeichenfolge selbst nicht ändern, da Zeichenfolgen unveränderlich sind. Im zweiten Fall ändern Sie den Inhalt des Objekts, dessen Wert sich auf des Parameters bezieht. Das ändert nicht den Wert des Parameters selbst - es wird dieselbe Referenz sein. Um ein reales Beispiel zu geben, wenn jemand etwas an Ihr Haus liefert, das die Inhalte Ihres Hauses ändert, aber es ändert nicht die Adresse Ihres Hauses.

Wenn Sie die zweite Methode, um dies geändert:

public void ChangeMe(Person x) 
{ 
    x = new Person("Fred"); 
} 

dann würde der Anrufer keine Veränderung sehen. Das liegt näher an dem, was Sie mit einer Zeichenfolge tun - Sie veranlassen, dass sich der Parameter auf ein anderes Objekt bezieht, anstatt den Inhalt des vorhandenen Objekts zu ändern. Wenn Sie nun einen ref Parameter verwenden, wird die Variable, die vom Aufrufer als Argument verwendet wird, mit dem Parameter "aliased". Wenn Sie also den Wert des Parameters ändern, ändert sich auch der Wert des Arguments. Wenn wir also die letzte Methode wie folgt zu ändern:

public void ChangeMe(ref Person x) 
{ 
    x = new Person("Fred"); 
} 

dann:

Person y = new Person("Eric"); 
ChangeMe(ref y); 
Console.WriteLine(y.Name); 

dies auszudrucken "Fred".

Das Schlüsselkonzept zu verstehen ist, dass der Wert einer Variablen nie ein Objekt ist - es ist entweder ein Werttyp Wert oder eine Referenz. Wenn die Daten eines Objekts geändert werden, wird diese Änderung durch andere Referenzen sichtbar. Sobald Sie verstanden haben, dass das Kopieren einer Referenz nicht dasselbe ist wie das Kopieren eines Objekts, fällt der Rest relativ leicht.

+0

Danke. Mein Gehirn ist jetzt sehr entspannt. Darf ich mir die Freiheit nehmen, die folgenden Fragen zu stellen? 1) Kann ich meine benutzerdefinierte Klasse unveränderlich machen? Kann ich meiner Klasse einen Wert Typ (wie int) geben? 3) Was ist der Unterschied zwischen Objekt und Typ? ["Wert einer Variablen ist nie ein Objekt - es ist entweder ein Werttyp Wert oder eine Referenz"] – Lijo

+2

1. Ja, Sie können definitiv Ihre eigenen unveränderlichen Klassen machen.Stellen Sie einfach keine Möglichkeit zur Verfügung, es zu mutieren. 2. Sie können Ihre eigenen benutzerdefinierten Werttypen erstellen, aber sie sind keine Klassen - sie sind Strukturen. 3. Ich bin mir nicht sicher, ob ich die Frage wirklich verstehe - aber ein Werttyp ist etwas wie "die Zahl 3" oder "der Buchstabe A", während eine Referenz eine Art ist, ein Objekt anzusprechen, im Grunde - es ist eine zusätzliche Ebene von Indirektion. –

+0

Danke. Zum Schluss - stimmt das Folgende? 1. \t Wenn eine Klasse an eine Methode ohne Verwendung von "ref" übergeben wird, kopiert sie die Speicheradresse in ein neues Referenzobjekt. Daher werden die Werte im Speicherort aktualisiert, wenn eine Aktualisierung an dem Objekt vorgenommen wird. 2. \t Wenn eine Klasse an eine Methode übergeben wird, die "ref" verwendet, wird ihre tatsächliche Referenz an die Methode übergeben. Wenn ich den Verweis auf einen anderen Speicherort mache, erhalten wir Zugriff auf den neuen Speicherort. Der alte Speicherort wird nicht mehr erreichbar. Danke – Lijo

7

Person ist ein Referenztyp, also egal, ob Sie ref, out oder nichts verwenden, Sie können es immer innerhalb der Methode ändern. Sie übergeben das reale Personenobjekt niemals an die Methode, Sie übergeben den Zeiger als Referenz, aber nicht den tatsächlichen Person. Das Schlüsselwort ref ist nützlich mit Werttypen (wie structs, int, float, DateTime, ...). Es könnte auch mit Referenztypen verwendet werden, aber nur um Verhalten anzuzeigen, aber nicht erzwingen. Wenn Sie es mit Referenztypen verwenden, können Sie das Objekt ändern, auf das diese Referenz verweist.

+4

Ich bin mir nicht sicher, warum Sie sagen, dass das Schlüsselwort 'ref' nur für Werttypen nützlich ist. Wenn Sie es mit Referenztypen verwenden, können Sie das Objekt, auf das der Parameter zeigt, ändern, was unter bestimmten Umständen sehr nützlich sein kann. –

+1

Nicht ganz richtig, Sie können immer noch einen Referenztyp mit "ref" übergeben und es wird subtil anders, weil Sie damit ändern können, was der ursprüngliche Verweis auch zeigt. – Alconja

+0

@Mark, @Alconja, ich stimme dir zu und ich habe meinen Beitrag aktualisiert, um deinen Vorschlag zu reflektieren. –

0

Wenn Sie P an eine Testmethode übergeben, übergeben Sie dessen Position im Speicher und nicht die Kopie des Objekts. Referenz wird im Hauptteil der Methode aufgenommen und der ursprüngliche Wert wird geändert.

-1

Wenn Sie einen Referenztyp Argument an eine Methode übergeben, bedeutet dies, dass das Verfahren auf dieses Argument einen direkten Zugang hat nicht auf eine Kopie davon ....

So das Ergebnis geändert wird.

+0

Nein, es hat eine Kopie des Arguments - aber dieses Argument ist eine Referenz, nicht das Objekt selbst. –

+0

Sie meinen, die Adresse eines Objekts wird kopiert ... oder? Was ich mit Direktzugriff meinte, war der gleiche wie bei Ihnen. nicht wahr? – odiseh

0

Utilityclass.TestMethod kann die lokale Variable p ändern zu einem anderen Person Objekt zu zeigen, da Sie nicht durch Bezugnahme sind vorbei, aber es ist nach wie vor frei, alle Methoden aufrufen oder alle Eigenschaften für das Objekt ändern Sie es übergeben wird. So kann die Name Eigenschaft innerhalb Utilityclass.TestMethod geändert werden.

0

Diese Frage meist beantwortet wurde, aber ich denke, Sie das Snippet ausprobieren vielleicht gefallen

class Program 
{ 
    static void Main(string[] args) 
    {    
     Person p = new Person(); 
     p.Name = "Class Lijo"; 

     Utilityclass.TestMethod(p); 
     string classResult = p.Name; 
     Console.WriteLine(classResult); 
     Utilityclass.TestMethod2(ref p); 
     classResult = p.Name; // will bomb here   
     Console.WriteLine(classResult); 
    } 
} 

public class Utilityclass 
{ 
    public static void TestMethod(Person k) 
    { 
     k.Name = "Changed"; 
     k = null; 
    } 

    public static void TestMethod2(ref Person k) 
    { 
     k.Name = "Changed Again!"; 
     k = null; 
    } 
}