Неглубокая копия или глубокая копия?
Я немного новичок в этих двух методах копирования одного объекта в другой. Я смущен и не в состоянии выявить основную разницу между глубокой копией и мелкой копией. Я рассмотрел множество теорий относительно этого, но мне нужно объяснение с надлежащими примерами.
У меня есть программа, в которой я копирую один объект в другой. →
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();
}
}
Это мелкая копия или глубокая копия? Может ли кто-нибудь объяснить причину. Если это глубокая копия, пожалуйста, предоставьте код для мелкой копии для этой программы, выполняющей ту же работу по копированию объекта, и наоборот.
Если выше это мелкая копия, то даже это должно быть мелкая копия →
A ob1 = new A();
ob1.a = 10;
ob1.display();
A ob2 = ob1;
ob2.a = 444;
ob1.display();
Ответы
Ответ 1
Из ссылки здесь
Мелкие копии дублируют как можно меньше. Неглубокая копия коллекция - это копия структуры коллекции, а не элементов. С мелкой копией две коллекции теперь разделяют человека элементы.
Глубокие копии дублируют все. Глубокая копия коллекции - две коллекции со всеми элементами в оригинальной коллекции дублируется.
В вашем примере создается мелкая копия.
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.
Глубокая копия будет -
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.
Ответ 2
По-моему, это не строгая мелкая копия или глубокая копия. Если я должен определить его, я бы сказал, мелкая копия.
ob2 = ob1;
Этот код создает две ссылки на объекты, которые относятся к одному и тому же объекту. Поэтому любые изменения объекта, созданного с помощью ob1, будут отражены в последующих применениях ob2.
Пример из MSDN лучше объяснить различия для мелкой копии, глубокой копии и просто просто копии класса.
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);
}
}
Вот результаты:
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
Ответ 3
Это не мелкая и не глубокая копия, это справочная копия. Я объясню: есть 2 типа переменных: типы значений и ссылочные типы.
тип значения - это (именованное) местоположение в памяти компьютера, которое содержит фактическое значение переменной. например: int - тип значения, поэтому, когда вы пишете эту строку кода:
int MyInt = 5;
когда эта строка кода будет выполнена, среда выполнения найдет местоположение в ОЗУ и напишет в нем значение 5. поэтому, если вы будете искать это местоположение, вы найдете фактическое значение 5.
ссылочный тип - в отличие - это (названное) местоположение в памяти, которое фактически не удерживает значение переменной, но удерживает местоположение памяти там, где это значение существует. в качестве примера предположим, что вы написали следующий код:
MyClass myObject = new MyClass();
происходит то, что виртуальная машина (среда выполнения):
1. Найдите и найдите доступное место в памяти, создайте экземпляр класса MyClass. скажем, что местоположение этого объекта оказалось в байте # AA3D2 в ОЗУ.
2- найти место в памяти и создать ссылку типа MyClass (ссылка - это "стрелка", указывающая на местоположение в памяти), назовите ее "myObject" и сохраните в ней значение AA3D2.
теперь, если вы посмотрите на переменную "myObject", вы не найдете экземпляр класса, но вы найдете AA3D2, которые представляют местоположение памяти, в которой хранится этот экземпляр класса.
теперь рассмотрим код, заданный моим OP:
A ob1 = new A();
это сделает переменную с именем ob1, создаст экземпляр класса A и сохранит местоположение этого класса в ob1
ob1.a = 10;
ob1.display();
это изменит переменную a, которая находится внутри класса A. Затем он вызывает метод display()
A ob2 = new A();
здесь он создает переменную с именем ob2, создает экземпляр класса A и назначает его местоположение ob2.
теперь у вас есть в памяти 2 экземпляра класса A и 2 переменных, каждый из которых указывает на один из них.
теперь вот интересная часть: ob2 = ob1;
переменной ob2 присваивается значение переменной ob1. поскольку ob1 содержит расположение памяти первого экземпляра A, теперь оба ob1 и ob2 указывают на одно и то же место в памяти. делая что-либо, используя один из них, точно делает то же самое с другим.
ob2 = ob1 означает, что вы копируете ссылку.
Ответ 4
Это мелкая копия, потому что если вы измените переменную ob2, а затем попробуйте и напечатайте ob1 - они будут одинаковыми. Это потому, что вещи в С#, которые являются классами, создают связи между собой. Если вы хотите сделать глубокую копию, вы должны применить метод копирования и скопировать поля вручную. Что-то вроде:
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;
}
}
Ответ 5
Напишите еще пару строк кода, чтобы изменить свойство первого объекта после его назначения второму объекту. Затем вызовите метод отображения на оба объекта и посмотрите, какие результаты. Это покажет вам, что на самом деле это мелкая копия.
Ответ 6
Я подтверждаю ответ от @docesam и часть ответа от @Will Yu.
Это не мелкая и не глубокая копия, это справочная копия. - docesam
ob2 = ob1; Этот код создает две ссылки на объекты, которые относятся к одному и тому же объекту. Поэтому любые изменения объекта, созданного с помощью ob1, будут отражены в последующих применениях ob2. - Будет Yu
В соответствии с MSDN (см. примечания):
Неглубокая копия массива копирует только элементы массива, независимо от того, являются ли они ссылочными типами или типами значений, но не копирует объекты, на которые ссылаются ссылки. Ссылки в новом массиве указывают на те же объекты, что и ссылки в исходном массиве.
Здесь мы можем отметить две вещи:
- Элементы мелкой копии копируют.
- Неглубокая копия сохраняет исходные ссылки элементов.
Далее, позвольте мне объяснить эти два отдельно.
Для начала создадим класс Person
со свойством Name
:
class Person
{
public string Name {get; set;}
}
Затем в методе Main()
мы создаем массив 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. Мелкие копии копируют элементы.
Если заменить первый элемент в мелкой копии, исходный массив не должен быть затронут:
// 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}" );
Результаты:
The Original Array: Jack, Amy
The Shallow Copy: Peter, Amy
2. Неглубокая копия сохраняет исходные ссылки элементов.
Если мы изменим свойства элемента в неглубокой копии, будет затронут исходный массив, поскольку объект, который этот элемент ссылается на него, не копируется.
// 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}" );
Результаты:
The Original Array: Peter, Amy
The Shallow Copy: Peter, Amy
Итак, как выглядит простой знак равенства =
?
Он делает справочную копию. Любое изменение элементов или упомянутых объектов будет отражено как в исходном массиве, так и в "скопированном" массиве.
// 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}" );
Результаты:
The Original Array: NameChanged, PersonChanged
The Reference Copy: NameChanged, PersonChanged
В заключение, ob2 = ob1
не является мелкой копией, а копией ссылки.
Полный код для воспроизведения:
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;}
}