2010-05-26 1 views
5

Potrebbe spiegare il seguente comportamento della classe C#. Mi aspetto che classResult sia "Class Lijo"; ma il valore attuale è "Modificato".lPassare in base al valore e passare per riferimento

Stiamo facendo una copia del riferimento. Sebbene la copia stia puntando allo stesso indirizzo, il metodo che riceve l'argomento non può cambiare l'originale.

Ancora perché il valore viene modificato?

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; 
    } 
} 

Aggiornamento: Quando passa una stringa, non ottiene effettivamente cambiato.

+3

Potrei suggerire di leggere l'eccellente articolo di [Mr Skeet sull'argomento] (http://www.yoda.arachsys.com/csharp/parameters.html). – Alconja

+0

Anche se String è un oggetto, è immutabile, quindi quando passi una stringa il riferimento originale non viene modificato – trendl

+0

Ho letto questo [articolo] (http://zuta-developer.blogspot.com/2012/06/net- parameter-passing.html) Questo articolo ti mostrerà come il passaggio dei parametri viene gestito in memoria. – Developex

risposta

22

La risposta più breve è (punti bonus se si tenta questo con interi!): Leggere my article on parameter passing che va in questo in una buona quantità di dettagli.

La risposta leggermente più lungo è quello di confrontare questi due metodi, entrambi i quali utilizzano valore parametri:

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

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

Nel primo caso, si sta cambiando il valore del parametro . Questo è completamente isolato dall'argomento originale. Non è possibile modificare il contenuto della stringa stessa, perché le stringhe sono immutabili.

Nel secondo caso, si modifica il contenuto di dell'oggetto a cui il valore del parametro fa riferimento. Questo non sta cambiando il valore del parametro stesso: sarà lo stesso riferimento. Per dare un esempio del mondo reale, se qualcuno consegna qualcosa a casa tua che cambia il contenuto della tua casa, ma non cambia l'indirizzo della tua casa.

Se è stato modificato il secondo metodo a questo:

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

poi il chiamante non avrebbe visto alcun cambiamento. Questo è più vicino a quello che stai facendo con una stringa: stai facendo in modo che il parametro si riferisca a un oggetto diverso, invece di modificare il contenuto dell'oggetto esistente.

Ora, quando si utilizza un parametro ref, la variabile utilizzata dal chiamante come argomento è "alias" con il parametro, quindi se si modifica il valore del parametro, anche il valore dell'argomento cambia. Quindi, se cambiamo l'ultimo metodo come questo:

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

poi:

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

questo stampa "Fred".

Il concetto chiave da comprendere è che il valore di una variabile non è mai un oggetto: è un valore di tipo valore o un riferimento. Se i dati di un oggetto vengono modificati, tale modifica sarà visibile attraverso altri riferimenti. Una volta compreso che copiare un riferimento non equivale a copiare un oggetto, il resto va a posto ragionevolmente.

+0

Grazie. Il mio cervello è molto rilassato ora. Posso prendere la libertà di porre le seguenti domande? 1) Posso rendere immutabile la mia classe personalizzata? Posso rendere la mia classe un valore Type (come int)? 3) Qual è la differenza tra oggetto e tipo? ["il valore di una variabile non è mai un oggetto - è un valore di tipo valore o un riferimento"] – Lijo

+2

1. Sì, puoi sicuramente rendere le tue classi immutabili.Basta non fornire alcun modo di mutarlo. 2. Puoi creare i tuoi tipi di valore personalizzati, ma non saranno classi: saranno strutture. 3. Non sono sicuro di capire veramente la domanda - ma un valore di tipo value è qualcosa come "il numero 3" o "la lettera A" mentre un riferimento è un modo di affrontare un oggetto, in pratica - è un livello extra di indirezione. –

+0

Grazie. Per concludere - è il seguente corretto? 1. \t Quando una classe viene passata a un metodo senza utilizzare "ref", copia l'indirizzo di memoria in un nuovo oggetto di riferimento. Quindi i valori nella posizione di memoria verranno aggiornati se viene apportato qualche aggiornamento all'oggetto. 2. \t Quando una classe viene passata a un metodo che utilizza "ref", il suo riferimento effettivo viene passato al metodo. Se faccio riferimento a una posizione diversa, accediamo alla nuova posizione di memoria. La vecchia posizione di memoria diventa irraggiungibile. Grazie – Lijo

7

Person è un tipo di riferimento, quindi non importa se si utilizza ref, out o nulla, sarà sempre possibile modificarlo all'interno del metodo. Non si passa mai l'oggetto persona reale al metodo, si passa il puntatore come riferimento ma non l'attuale Person. La parola chiave ref è utile con i tipi di valore (come structs, int, float, DateTime, ...). Potrebbe anche essere utilizzato con i tipi di riferimento ma solo per indicare il comportamento ma non per rafforzarlo. Se lo si utilizza con i tipi di riferimento, consente di modificare l'oggetto a cui punta il riferimento.

+4

Non sono sicuro del perché dici "la parola chiave 'ref' è utile solo per i tipi di valore". Se lo si utilizza con i tipi di riferimento, consente di modificare l'oggetto a cui fa riferimento il parametro, il che può essere molto utile in alcune circostanze. –

+1

Non proprio vero, puoi ancora passare un tipo di riferimento con 'ref' e sarà sottilmente differente perché ti permetterà di cambiare anche ciò che sta puntando il riferimento originale. – Alconja

+0

@Mark, @Alconja, sono d'accordo con te e ho aggiornato il mio post per riflettere il tuo suggerimento. –

0

Quando si passa P a un metodo di prova si passa la sua posizione nella memoria, piuttosto che la copia dell'oggetto. Il riferimento viene rilevato nel corpo del metodo e il valore originale viene modificato.

-1

Quando si passa un argomento di tipo riferimento ad un metodo, questo significa che il metodo ha accesso diretto a tale argomento non una copia di esso ....

Così il risultato è cambiato.

+0

No, ha una copia dell'argomento - ma quell'argomento è un riferimento, non l'oggetto stesso. –

+0

Vuoi dire che l'indirizzo di un oggetto è stato copiato ... giusto? Quello che intendevo per accesso diretto era lo stesso del tuo. non è vero? – odiseh

0

Utilityclass.TestMethod non può modificare la variabile locale p punti a un diverso Person oggetto dal momento che non sta passando per riferimento, ma è ancora libero di chiamare qualsiasi metodo o modificare le proprietà sull'oggetto è passato. Pertanto la proprietà Name può essere modificata entro Utilityclass.TestMethod.

0

Questa domanda è stata in gran parte risposto, ma penso che si potrebbe provare questo frammento

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; 
    } 
}