Considere este código:
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; }
}
Obviamente, cuando se asigna person1
a person2
y la Name
propiedad de person2
está cambiado, el Name
del person1
también será cambiado. person1
y person2
tener la misma referencia.
¿Por qué cuando person2 = null
, la person1
variable tampoco será nula?
person1
yperson2
son 2 tarjetas de presentación diferentes que contienen una dirección. Si lo haceperson1 = person2
,person1
ahora es una copia deperson2
. Siguen siendo tarjetas de visita diferentes , pero ambas contienen la misma dirección apuntando hacia el mismo objeto. El objeto en sí no cambia.person2
en la segunda imagen porque no hace referencia a nada, no es una referencia colgante.Considere
person1
yperson2
como indicadores de algún lugar de almacenamiento. En el primer paso, soloperson1
se mantiene la dirección del objeto del almacenamiento y luegoperson2
se mantiene la dirección de la ubicación de memoria del objeto del almacenamiento. Más tarde, cuando asignenull
aperson2
,person1
no se verá afectado. Por eso ves el resultado.Puede leer: Valor frente a tipos de referencia de Joseph Albahari
Intentaré representar el mismo concepto utilizando el siguiente diagrama.
Se creó un nuevo objeto de tipo persona y la
person1
referencia (puntero) apunta a la ubicación de la memoria en el almacenamiento.Se creó una nueva referencia (puntero)
person2
que apunta a la misma en el almacenamiento.Se cambió la propiedad del objeto Name a un nuevo valor,
person2
ya que ambas referencias apuntan al mismo objeto,Console.WriteLine(person1.Name);
salidasShahrooz
.Después de asignar
null
unaperson2
referencia, no apuntará a nada, peroperson1
seguirá manteniendo la referencia al objeto.(Finalmente, para la gestión de la memoria, debería ver La pila es un detalle de implementación, Parte uno y La pila es un detalle de implementación, Parte dos de Eric Lippert)
fuente
person
se almacenaría en el montón y sus referenciasperson1
yperson2
estaría en la pila. Pero estoy de acuerdo en que es más un detalle de implementación. Pero tener stack and heap en la respuesta (como el artículo de albahari) lo dejaría más claro en mi opinión.Ha cambiado
person2
a la referencianull
, peroperson1
no está haciendo referencia allí.Lo que quiero decir es que si miramos
person2
yperson1
antes de la asignación, ambos hacen referencia al mismo objeto. Luego asignaperson2 = null
, por lo que la persona 2 ahora hace referencia a un tipo diferente. No eliminó el objeto al queperson2
se hacía referencia.He creado este gif para ilustrarlo:
fuente
Porque ha establecido la referencia a
null
.Cuando establece una referencia a
null
, la referencia en sí esnull
... no el objeto al que hace referencia.Piense en ellos como una variable que tiene un desplazamiento de 0.
person
tiene el valor 120.person2
tiene el valor 120. Los datos en el desplazamiento 120 son elPerson
objeto. Cuando haces esto:person2 = null;
..you're diciendo efectivamente,
person2 = 0;
. Sin embargo,person
todavía tiene el valor 120.fuente
copy-by-value
semántica. Estás copiando el valor de la referencia.Ambos
person
yperson2
apuntan al mismo objeto. Por lo tanto, cuando cambie el nombre de cualquiera de ellos, ambos cambiarán (ya que apuntan a la misma estructura en la memoria).Pero cuando se establece
person2
ennull
, se convierteperson2
en un puntero nulo, por lo que ya no apunta al mismo objetoperson
. No le hará nada al objeto en sí para destruirlo, y dado queperson
todavía apunta / hace referencia al objeto, tampoco lo matará la recolección de basura.Si también establece
person = null
, y no tiene otras referencias al objeto, el recolector de basura eventualmente lo eliminará.fuente
person1
yperson2
apunte a la misma dirección de memoria. Cuando anulaperson2
, anula la referencia, no la dirección de memoria, por lo queperson1
continúa refiriéndose a esa dirección de memoria, esa es la razón. Si cambiaClasss Person
a aStruct
, el comportamiento cambiará.fuente
Me parece más útil pensar en los tipos de referencia como si tuvieran ID de objeto. Si uno tiene una variable de tipo de clase
Car
, la declaraciónmyCar = new Car();
le pide al sistema que cree un auto nuevo e informe su ID (digamos que es el objeto # 57); luego pone "objeto # 57" en variablemyCar
. Si uno escribeCar2 = myCar;
, escribe "objeto # 57" en la variable Car2. Si uno escribecar2.Color = blue;
, eso indica al sistema que busque el automóvil identificado por Car2 (por ejemplo, el objeto # 57) y lo pinte de azul.Las únicas operaciones que se realizan directamente en los ID de objeto son la creación de un nuevo objeto y obtener el ID, obtener un ID "en blanco" (es decir, nulo), copiar un ID de objeto a una variable o ubicación de almacenamiento que pueda contenerlo, verificar si hay dos los ID de objeto coinciden (hacen referencia al mismo objeto). Todas las demás solicitudes le piden al sistema que encuentre el objeto al que hace referencia un ID y actúe sobre ese objeto (sin afectar la variable u otra entidad que tenía el ID).
En las implementaciones existentes de .NET, es probable que las variables de objeto contengan punteros a objetos almacenados en un montón de basura recolectada, pero ese es un detalle de implementación inútil porque hay una diferencia crítica entre una referencia de objeto y cualquier otro tipo de puntero. Generalmente se supone que un puntero representa la ubicación de algo que permanecerá el tiempo suficiente para trabajar con él. Las referencias a objetos no lo hacen. Un fragmento de código puede cargar el registro SI con una referencia a un objeto ubicado en la dirección 0x12345678, comenzar a usarlo y luego ser interrumpido mientras el recolector de basura mueve el objeto a la dirección 0x23456789. Eso sonaría como un desastre, pero la basura examinará los metadatos asociados con el código, observará que el código usó SI para contener la dirección del objeto que estaba usando (es decir, 0x12345678), determine que el objeto que estaba en 0x12345678 se ha movido a 0x23456789 y actualice SI para que contenga 0x23456789 antes de que regrese. Tenga en cuenta que en ese escenario, el recolector de basura cambió el valor numérico almacenado en SI, pero se refería ael mismo objeto antes de la mudanza y después. Si antes del movimiento se refería al objeto número 23 592 creado desde el inicio del programa, continuará haciéndolo después. Curiosamente, .NET no almacena ningún identificador único e inmutable para la mayoría de los objetos; dadas dos instantáneas de la memoria de un programa, no siempre será posible saber si algún objeto en particular en la primera instantánea existe en la segunda, o si se han abandonado todos los rastros y se ha creado un nuevo objeto que se parece a él en todos los detalles observables.
fuente
person1 y person2 son dos referencias separadas en la pila que apuntan al mismo objeto Person en el montón.
Cuando elimina una de las referencias, se elimina de la pila y ya no apunta al objeto Persona en el montón. La otra referencia permanece, aún apuntando al objeto Person existente en el montón.
Una vez que se eliminan todas las referencias al objeto Persona, eventualmente el recolector de basura eliminará el objeto de la memoria.
fuente
Cuando crea un tipo de referencia, en realidad está copiando una referencia con todos los objetos apuntando a la misma ubicación de memoria, sin embargo, si ha asignado Person2 = Null, no tendrá ningún efecto ya que person2 es solo una copia de la persona de referencia y acabamos de borrar una copia de referencia .
fuente
Tenga en cuenta que puede obtener la semántica de valor cambiando a
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; } }
fuente
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; } }
fuente
Primero copia la referencia
person1
aperson2
. Ahoraperson1
yperson2
haga referencia al mismo objeto, lo que significa queName
se pueden observar modificaciones en el valor de ese objeto (es decir, cambiar la propiedad) en ambas variables. Luego, al asignar un valor nulo, está eliminando la referencia que acaba de asignarperson2
. Solo está asignado aperson1
ahora. Tenga en cuenta que la referencia en sí no es modifica.Si tuviera una función que aceptara un argumento por referencia, podría pasar
reference to person1
por referencia y podría cambiar la referencia en sí: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;} }
fuente
Diciendo:
person2.Name = "Shahrooz";
sigue la referencia de
person2
y "muta" (cambia) el objeto al que la referencia pasa a conducir. La referencia en sí (el valor deperson2
) no se modifica; seguimos haciendo referencia a la misma instancia en la misma "dirección".Asignar a
person2
como en:person2 = null;
cambia la referencia . Ningún objeto se cambia. En este caso, la flecha de referencia es "movido" de un objeto a "nada",
null
. Pero una asignación comoperson2 = new Person();
también solo cambiaría la referencia. Ningún objeto está mutado.fuente