2010-05-26 22 views
5

Podría explicar el siguiente comportamiento de C# Class. Espero classResult como "Class Lijo"; pero el valor real es "Cambiado".lPass por valor & Pase por referencia

Estamos haciendo una copia de la referencia. Aunque la copia apunta a la misma dirección, el método que recibe el argumento no puede cambiar el original.

¿Por qué cambia el valor?

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

actualización: Cuando pasa una cadena, que no consigue realmente cambió.

+3

Podría sugerir tener una lectura del [excelente artículo del señor Skeet sobre el tema] (http://www.yoda.arachsys.com/csharp/parameters.html). – Alconja

+0

Aunque String es un objeto, es inmutable, por lo que cuando se pasa una cadena, la referencia original no se modifica – trendl

+0

He leído este [artículo] (http://zuta-developer.blogspot.com/2012/06/net- parameter-passing.html) Este artículo le mostrará cómo se maneja el paso de parámetros en la memoria. – Developex

Respuesta

22

La respuesta más breve es (puntos de bonificación si se intenta esto con enteros!): Leer my article on parameter passing que va en esto en una buena cantidad de detalles.

La respuesta es un poco más largo para comparar estos dos métodos, los cuales utilizan valor parámetros:

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

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

En el primer caso, va a cambiar el valor del parámetro . Eso está completamente aislado del argumento original. No puede cambiar el contenido de la cadena en sí, porque las cadenas son inmutables.

En el segundo caso, está cambiando el contenido del objeto cuyo valor de parámetro se refiere a. Eso no cambia el valor del parámetro en sí; será la misma referencia. Para dar un ejemplo del mundo real, si alguien entrega algo a su casa que cambia el contenido de su casa, pero no cambia la dirección de su casa.

Si ha cambiado el segundo método para esto:

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

entonces la persona que llama no vería ningún cambio. Esto está más cerca de lo que está haciendo con una cadena: hace que el parámetro haga referencia a un objeto diferente, en lugar de cambiar el contenido del objeto existente.

Ahora, cuando utiliza un parámetro ref, la variable utilizada por la persona que llama como argumento tiene "alias" con el parámetro, por lo que si cambia el valor del parámetro, también cambia el valor del argumento. Así que si cambiamos el último método como este:

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

a continuación:

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

esto mostrara "Fred".

El concepto clave para comprender es que el valor de una variable nunca es un objeto, ya sea un valor de tipo de valor o una referencia. Si se modifican los datos de un objeto, ese cambio será visible a través de otras referencias. Una vez que comprenda que copiar una referencia no es lo mismo que copiar un objeto, el resto entra en su lugar con bastante facilidad.

+0

Gracias. Mi cerebro está muy relajado ahora. ¿Puedo tomar la libertad de hacer las siguientes preguntas? 1) ¿Puedo hacer que mi clase personalizada sea inmutable? ¿Puedo hacer que mi clase sea un valor Tipo (como int)? 3) ¿Cuál es la diferencia entre objeto y tipo? ["el valor de una variable nunca es un objeto, es un valor de tipo de valor o una referencia"] – Lijo

+2

1. Sí, definitivamente puede hacer sus propias clases inmutables.Simplemente no proporciones ninguna forma de mutarlo. 2. Puede crear sus propios tipos de valores personalizados, pero no serán clases; serán estructuras. 3. No estoy seguro de entender realmente la pregunta, pero un valor de tipo de valor es algo así como "el número 3" o "la letra A", mientras que una referencia es una forma de abordar un objeto, básicamente, es un nivel extra de indirección –

+0

Gracias. Para concluir, ¿es correcto lo siguiente? 1. \t Cuando se pasa una clase a un método sin usar "ref", está copiando la dirección de memoria a un nuevo objeto de referencia. Por lo tanto, los valores en la ubicación de la memoria se actualizarán si se realiza alguna actualización en el objeto. 2. \t Cuando se pasa una clase a un método que utiliza "ref", su referencia real se pasa al método. Si hago la referencia para señalar una ubicación diferente, entonces obtendremos acceso a la nueva ubicación de la memoria. La ubicación de memoria anterior se vuelve inalcanzable. Gracias – Lijo

7

Person es un tipo de referencia, así que no importa si usa ref, out o nada, siempre podrá modificarlo dentro del método. Nunca pasa el objeto de persona real al método, está pasando el puntero como referencia, pero no el Person real. La palabra clave ref es útil con tipos de valores (como structs, int, float, DateTime, ...). También se podría usar con tipos de referencia, pero solo para indicar el comportamiento pero no para aplicarlo. Si lo usa con tipos de referencia, le permite cambiar el objeto al que apunta esta referencia.

+4

No estoy seguro de por qué dices "la palabra clave' ref' es útil solo para tipos de valores ". Si lo usa con tipos de referencia, le permite cambiar el objeto al que apunta el parámetro, que puede ser muy útil en algunas circunstancias. –

+1

No del todo cierto, aún puede pasar un tipo de referencia por 'ref' y será sutilmente diferente porque le permitirá cambiar lo que apunta también la referencia original. – Alconja

+0

@Mark, @Alconja, estoy de acuerdo con usted y he actualizado mi publicación para reflejar su sugerencia. –

0

Cuando pasa P a un método de prueba, pasa su ubicación en la memoria, en lugar de la copia del objeto. La referencia se recoge en el cuerpo del método y el valor original se modifica.

-1

Cuando se pasa un argumento del tipo de referencia a un método, esto significa que el método tiene acceso directo a ese argumento no a una copia de la misma ....

Así, el resultado se cambia.

+0

No, tiene una copia del argumento, pero ese argumento es una referencia, no el objeto en sí. –

+0

Quiere decir que la dirección de un objeto se copia ... ¿verdad? Lo que quise decir con acceso directo fue el mismo que el tuyo. ¿no? – odiseh

0

Utilityclass.TestMethod no se puede cambiar la variable local p para apuntar a un objeto diferente Person ya que no está de paso por referencia, pero todavía es libre de llamar a cualquier método o cambiar cualquier propiedad en el objeto que se pasa. Por lo tanto, la propiedad Name se puede modificar dentro de Utilityclass.TestMethod.

0

Esta pregunta ha sido sobre todo contestado, pero creo que le gustaría probar este fragmento

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