c# - ¿Copia superficial o copia profunda?
deep-copy shallow-copy (6)
Desde el enlace here
Copias poco profundas se duplican lo menos posible. Una copia superficial de una colección es una copia de la estructura de la colección, no los elementos. Con una copia superficial, dos colecciones ahora comparten los elementos individuales.
Copias profundas duplican todo. Una copia profunda de una colección son dos colecciones con todos los elementos de la colección original duplicados.
Tu ejemplo es crear una copia superficial.
A ob1 = new A();
ob1.a = 10;
A ob2 = new A();
ob2 = ob1;
ob1.a = 5; // <-- If you see value of ob2.a after this line, it will be 5.
Copia profunda será -
A ob1 = new A();
ob1.a = 10;
A ob2 = new A();
ob2.a = ob1.a;
ob1.a = 5; // <-- If you see value of ob2.a after this line, it will be 10.
Soy un poco nuevo en estos dos métodos de copiar un objeto en el otro. Estoy confundido e incapaz de detectar la gran diferencia entre copia profunda y copia superficial. Había pasado por mucha teoría al respecto, pero necesito una explicación con ejemplos adecuados. Tengo un programa en el que copio un objeto en otro . ->
class A
{
public int a = 0;
public void display()
{
Console.WriteLine("The value of a is " + a);
}
}
class Program
{
static void Main(string[] args)
{
A ob1 = new A();
ob1.a = 10;
ob1.display();
A ob2 = new A();
ob2 = ob1;
ob2.display();
Console.Read();
}
}
¿Es esta una copia superficial o una copia profunda? ¿Alguien por favor puede proporcionar la respuesta con razón. Si se trata de una copia profunda, proporcione el código de copia superficial para este programa que realiza el mismo trabajo de copia de objetos, y viceversa.
Si lo anterior es una copia superficial, entonces incluso esta debería ser una copia superficial ->
A ob1 = new A();
ob1.a = 10;
ob1.display();
A ob2 = ob1;
ob2.a = 444;
ob1.display();
En mi opinión, no es una copia superficial estricta o copia profunda. Si tengo que definirlo, diría una copia superficial.
ob2 = ob1; Este código crea dos referencias de objeto que se refieren al mismo objeto. Por lo tanto, cualquier cambio en el objeto realizado a través de ob1 se reflejará en los usos posteriores de ob2.
Un ejemplo de MSDN sería mejor para explicar las diferencias entre copia superficial, copia profunda y simplemente copia de clase.
using System;
public class IdInfo
{
public int IdNumber;
public IdInfo(int IdNumber)
{
this.IdNumber = IdNumber;
}
}
public class Person
{
public int Age;
public string Name;
public IdInfo IdInfo;
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
public Person DeepCopy()
{
Person other = (Person)this.MemberwiseClone();
other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
other.Name = String.Copy(this.Name);
return other;
}
}
public class Example
{
public static void Main()
{
// Create an instance of Person and assign values to its fields.
Person p1 = new Person();
p1.Age = 42;
p1.Name = "Sam";
p1.IdInfo = new IdInfo(6565);
// Perform a shallow copy of p1 and assign it to p2.
Person p2 = (Person)p1.ShallowCopy();
// Display values of p1, p2
Console.WriteLine("Original values of p1 and p2:");
Console.WriteLine(" p1 instance values: ");
DisplayValues(p1);
Console.WriteLine(" p2 instance values:");
DisplayValues(p2);
// Change the value of p1 properties and display the values of p1 and p2.
p1.Age = 32;
p1.Name = "Frank";
p1.IdInfo.IdNumber = 7878;
Console.WriteLine("/nValues of p1 and p2 after changes to p1:");
Console.WriteLine(" p1 instance values: ");
DisplayValues(p1);
Console.WriteLine(" p2 instance values:");
DisplayValues(p2);
// Make a deep copy of p1 and assign it to p3.
Person p3 = p1.DeepCopy();
// Change the members of the p1 class to new values to show the deep copy.
p1.Name = "George";
p1.Age = 39;
p1.IdInfo.IdNumber = 8641;
Console.WriteLine("/nValues of p1 and p3 after changes to p1:");
Console.WriteLine(" p1 instance values: ");
DisplayValues(p1);
Console.WriteLine(" p3 instance values:");
DisplayValues(p3);
// Make an equal of p1 and assign it to p4.
Person p4 = new Person();
p4 = p1;
// Change the members of the p1 class to new values to show the equal copy.
p1.Name = "Will";
p1.Age = 30;
p1.IdInfo.IdNumber = 8484;
Console.WriteLine("/nValues of p1 and p4 after changes to p1:");
Console.WriteLine(" p1 instance values: ");
DisplayValues(p1);
Console.WriteLine(" p4 instance values:");
DisplayValues(p4);
}
public static void DisplayValues(Person p)
{
Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
}
}
Aquí están los resultados:
Original values of p1 and p2: p1 instance values:
Name: Sam, Age: 42
Value: 6565 p2 instance values:
Name: Sam, Age: 42
Value: 6565
Values of p1 and p2 after changes to p1: p1 instance values:
Name: Frank, Age: 32
Value: 7878 p2 instance values:
Name: Sam, Age: 42
Value: 7878
Values of p1 and p3 after changes to p1: p1 instance values:
Name: George, Age: 39
Value: 8641 p3 instance values:
Name: Frank, Age: 32
Value: 7878
Values of p1 and p4 after changes to p1: p1 instance values:
Name: Will, Age: 30
Value: 8484 p4 instance values:
Name: Will, Age: 30
Value: 8484
Es una copia superficial porque si modifica la variable de ob2 (y luego intenta imprimir ob1) serán las mismas. Esto se debe a que las cosas en C # que son clases crean vínculos entre sí. Si desea hacer una copia en profundidad, debe implementar un método de copia y copiar los campos a mano. Algo como:
class A
{
public int a = 0;
public void display()
{
Console.WriteLine("The value of a is " + a);
}
public A Copy()
{
A a = new A();
a.a = = this.a;
return a;
}
}
Escriba un par de líneas más de código para cambiar la propiedad del primer objeto después de asignarlo al segundo objeto. Luego llame al método de visualización en ambos objetos y vea cuáles son los resultados. Esto te revelará que de hecho es una copia superficial.
Esto no es ni una copia superficial ni profunda, es una copia de referencia. Explíqueme: hay 2 tipos de variables: tipos de valores y tipos de referencias.
un tipo de valor es una ubicación (con nombre) en la memoria de la computadora que contiene el valor real de la variable. por ejemplo: int es un tipo de valor, así que cuando escriba esta línea de código:
int MyInt = 5;
cuando esta línea de código se ejecute, el tiempo de ejecución encontrará una ubicación en la RAM y escribirá el valor 5 en ella. por lo que si busca esa ubicación encontrará un valor real de 5.
un tipo de referencia, en contraste, es una ubicación (nombrada) en la memoria que no tiene realmente el valor de la variable, sino la ubicación de la memoria donde existe ese valor. como ejemplo supongamos que escribiste el siguiente código:
MyClass myObject = new MyClass();
lo que sucede es que la máquina virtual (tiempo de ejecución): 1- busca y encuentra una ubicación disponible en la memoria, crea una instancia de la clase MyClass. digamos que la ubicación de ese objeto está en el byte # AA3D2 en la RAM.
2- Encuentre una ubicación en la memoria y cree una referencia de tipo MyClass (una referencia es una "flecha" que apunta a una ubicación en la memoria), asígnele el nombre "myObject" y almacene el valor AA3D2 en ella.
Ahora, si observa la variable "myObject", no encontrará la instancia de la clase, sino la AA3D2, que representa la ubicación de la memoria que contiene esa instancia de la clase.
Ahora vamos a examinar el código dado mi OP:
A ob1 = new A();
esto creará una variable llamada ob1, creará una instancia de la clase A y almacenará la ubicación de esa clase en ob1
ob1.a = 10;
ob1.display();
esto cambiará la variable a que está dentro de la clase A. luego invoca el método display ()
A ob2 = new A();
aquí crea una variable llamada ob2, crea una instancia de la clase A y asigna su ubicación a ob2.
a estas alturas ya tienes en la memoria 2 instancias de clase de A y 2 variables que apuntan a una de ellas. Ahora aquí está la parte interesante: ob2 = ob1;
A la variable ob2 se le asigna el valor de la variable ob1. porque ob1 contiene la ubicación de memoria de la primera instancia de A, ahora tanto ob1 como ob2 apuntan a la misma ubicación en la memoria. hacer algo usando uno de ellos es exactamente hacer lo mismo con el otro.
ob2 = ob1 significa que está copiando la referencia.
Respaldo la respuesta de @docesam y parte de la respuesta de @Will Yu.
Esto no es ni una copia superficial ni profunda, esta es una copia de referencia. - docesam
ob2 = ob1; Este código crea dos referencias de objeto que se refieren al mismo objeto. Por lo tanto, cualquier cambio en el objeto realizado a través de ob1 se reflejará en los usos posteriores de ob2. - Will Yu
Según MSDN (ver Observaciones) :
Una copia superficial de un Array copia solo los elementos del Array, ya sean tipos de referencia o tipos de valor, pero no copia los objetos a los que se refieren las referencias. Las referencias en la nueva matriz apuntan a los mismos objetos a los que apuntan las referencias en la matriz original.
Aquí tenemos dos cosas a tener en cuenta:
- Una copia superficial copia elementos.
- Una copia superficial conserva las referencias originales de los elementos.
A continuación, déjame explicar estos dos por separado.
Para empezar, creamos una clase de Person
con una propiedad de Name
:
class Person
{
public string Name {get; set;}
}
Luego, en el método Main()
, creamos una matriz de Person
.
// Create 2 Persons.
var person1 = new Person(){ Name = "Jack" };
var person2 = new Person(){ Name = "Amy" };
// Create a Person array.
var arrPerson = new Person[] { person1, person2 };
1. Una copia superficial copia elementos.
Si reemplazamos el primer elemento en la copia superficial, la matriz original no debería verse afectada:
// Create a shallow copy.
var arrPersonClone = (Person[]) arrPerson.Clone();
// Replace an element in the shallow copy.
arrPersonClone[0] = new Person(){Name = "Peter"};
// Display the contents of all arrays.
Console.WriteLine( "After replacing the first element in the Shallow Copy" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
Resultados:
The Original Array: Jack, Amy
The Shallow Copy: Peter, Amy
2. Una copia superficial conserva las referencias originales de los elementos.
Si cambiamos las propiedades de un elemento en la copia superficial, la matriz original se verá afectada, ya que el objeto al que hace referencia este elemento no se copia.
// Create a new shallow copy.
arrPersonClone = (Person[]) arrPerson.Clone();
// Change the name of the first person in the shallow copy.
arrPersonClone[0].Name = "Peter";
// Display the contents of all arrays.
Console.WriteLine( "After changing the Name property of the first element in the Shallow Copy" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
Resultados:
The Original Array: Peter, Amy
The Shallow Copy: Peter, Amy
Entonces, ¿cómo se comporta un simple signo igual, =
,?
Hace una copia de referencia. Cualquier cambio en los elementos o en los objetos referidos se reflejará tanto en la matriz original como en la matriz "copiada".
// Create a reference copy.
var arrPersonR = arrPerson;
// Change the name of the first person.
arrPersonR[0].Name = "NameChanged";
// Replace the second person.
arrPersonR[1] = new Person(){ Name = "PersonChanged" };
// Display the contents of all arrays.
Console.WriteLine( "After changing the reference copy:" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Reference Copy: {arrPersonR[0].Name}, {arrPersonR[1].Name}" );
Resultados:
The Original Array: NameChanged, PersonChanged
The Reference Copy: NameChanged, PersonChanged
En conclusión , ob2 = ob1
no es una copia superficial sino una copia de referencia.
Código completo para jugar con:
void Main()
{
// Create 2 Persons.
var person1 = new Person(){ Name = "Jack" };
var person2 = new Person(){ Name = "Amy" };
// Create a Person array.
var arrPerson = new Person[] { person1, person2 };
// ----------- 1. A shallow copy copies elements. -----------
// Create a shallow copy.
var arrPersonClone = (Person[]) arrPerson.Clone();
// Replace an element in the shallow copy.
arrPersonClone[0] = new Person(){Name = "Peter"};
// Display the contents of all arrays.
Console.WriteLine( "After replacing the first element in the Shallow Copy:" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
Console.WriteLine( "/n" );
// ----------- 2. A shallow copy retains the original references of the elements. -----------
// Create a new shallow copy.
arrPersonClone = (Person[]) arrPerson.Clone();
// Change the name of the first person in the shallow copy.
arrPersonClone[0].Name = "Peter";
// Display the contents of all arrays.
Console.WriteLine( "After changing the Name property of the first element in the Shallow Copy:" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
Console.WriteLine( "/n" );
// ----------- 2. The equal sign. -----------
// Create a reference copy.
var arrPersonR = arrPerson;
// Change the name of the first person.
arrPersonR[0].Name = "NameChanged";
// Replace the second person.
arrPersonR[1] = new Person(){ Name = "PersonChanged" };
// Display the contents of all arrays.
Console.WriteLine( "After changing the reference copy:" );
Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
Console.WriteLine( $"The Reference Copy: {arrPersonR[0].Name}, {arrPersonR[1].Name}" );
}
class Person
{
public string Name {get; set;}
}