Strange result in Clone ()

I am learning C # Deep Copy and Shallow Copy. Here, after changing in demo_obj1, the value of the object changes, but the list is not updated, and in demo_obj2 the value of the object changes, and the value of the list is also updated. Does anyone know what is going on here? Thanks

Visual studio 2017

.Net framework 4.6

public class Demo : ICloneable { public int Value { get; set; } public string UID { get; set; } public Demo(int nValue) { Value = nValue; } public object Clone() { return this.MemberwiseClone(); } } public class Program { public static void Print(List<Demo> objList) { Console.WriteLine(); foreach (Demo objDemo in objList) { Console.WriteLine("{0} = {1}", objDemo.UID, objDemo.Value); } } public static void Main() { List<Demo> objList = new List<Demo>(); Demo obj1 = new Demo(100); obj1.UID = "Demo_obj1"; Demo obj2 = (Demo)obj1.Clone(); obj2.UID = "Demo_obj2"; objList.Add(obj1); objList.Add(obj2); Print(objList); obj1 = obj2; obj1.Value = 200; Console.WriteLine(); Console.WriteLine(obj1.UID + " = " + obj1.Value); Console.WriteLine(obj2.UID + " = " + obj2.Value); Print(objList); Console.ReadKey(); } } 

Output:

 Demo_obj1 = 100 Demo_obj2 = 100 Demo_obj2 = 200 Demo_obj2 = 200 Demo_obj1 = 100 Demo_obj2 = 200 
+8
c #
source share
4 answers

This has nothing to do with cloning; this is a link issue.

You created two objects obj1 and obj2 and put them in a list.
Now you iterate through the collection, outputting it and get the expected results.

Below are the links:

 obj1, list[0] -> Demo_obj1 (100) obj2, list[1] -> Demo_obj2 (100) Output list[0] => Demo_obj1 (100) Output list[1] => Demo_obj2 (100) 

Later, obj1 = obj2 , you assigned the link from obj2 to obj1 . You do not change its value or copy your object, you simply copy the link and point to another object.
So, in fact, both of them now point to the same object.
The list contains the same two links to different objects.

 list[0] -> Demo_obj1 (100) obj1, obj2, list[1] -> Demo_obj2 (100) 

Then you do obj2.Value = 200 , actually changing your value to 200:

 list[0] -> Demo_obj1 (100) obj1, obj2, list[1] -> Demo_obj2 (200) 

When you try to infer the identifiers and values ​​of obj1 and obj2 now, you will actually infer the value of the same object (Demo_obj2).

 Output obj1 => Demo_obj2 (200) Output obj2 => Demo_obj2 (200) 

However, if you try to iterate through the collection, you will again get Demo_obj1 and Demo_obj2 according to the link table.

 Output list[0] => Demo_obj1 (100) Ouptut list[1] => Demo_obj2 (200) 
+6
source share

So, I think there are a few points that you need to understand from this question.

Firstly, regarding your actual question, the clone() method really gives you 2 objects. They begin with a value of 100 and are added to the list. Please note that this list points to the objects contained in obj1 and obj2 and does not use the links you created.

Then you do the following:

 obj1 = obj2; obj1.Value = 200; 

What it is are updates that you reference obj1 to obj2 , and now they both point to the same object. You can see this when you register, and you see 200 twice. Please note that the pointers in your list are not updated, they are completely different pointers.

Finally, when you start your second log using the pointers in the list, you see the original obj1 with a value of 100 and the second obj2 that you updated to have a value of 200 .


Now the interesting thing is, this is actually not a good example of a deep clone, because you are using primitive values ​​that will be copied anyway. To achieve a better result, you probably want to wrap some values ​​inside the object:

 class Bar { public int Value; public Bar(int value) { this.value = value; } } class Foo : ICloneable { public String Id; public Bar MyBar; public Foo(int value) { this.MyBar = new Bar(value); } public object Clone() { return this.MemberwiseClone(); } } 

Now, if you had to build a panel and now make a shallow clone of Foo , it will still use the same Bar . So:

 Foo f = new Foo(100); Foo f2 = (Foo)f.Clone(); f2.MyBar.Value = 200; Console.WriteLine(f.MyBar.Value); // 200 Console.WriteLine(f2.MyBar.Value); // 200 

Here you need to get into Deep cloning so that each instance of Foo uses a different link to a unique Bar .

+5
source share

You set the variable obj1 as the instance contained in obj2 . The original value obj1 still exists in the list. in the line where you execute obj1 = obj2; , this sets the variable obj1 to store obj2 . it does not replace the value of the instance that was previously stored in obj1 . therefore, you see the conclusions that you are describing.

+3
source share

Forget MemberwiseClone , there is a red herring here.

Your problem is here:

 obj1 = obj2; obj1.Value = 200; 
  • Variables are placeholders for values.
  • The value stored in the referenced typed variable is the "address" in which the object referenced "lives" in memory.
  • Variables by default are copied by value in C #.

So this means that obj1 = obj2; obj1.Value = 200; obj1 = obj2; obj1.Value = 200; performs the following actions:

  • Get the value stored in obj2 (the address where the instance { "Demo_obj2"; 100 } lives.
  • Copy this value to the obj1 variable. Now the value of obj1 is the address in which the same instance lives { "Demo_obj2"; 100 } { "Demo_obj2"; 100 } ; both obj1 and obj2 point to the same object.
  • Change the Value object referenced by obj1 (or obj2 ) to 200 : the result is an instance of { "Demo_obj2"; 200 } { "Demo_obj2"; 200 } .
  • Instance { "Demo_obj1"; 100 } { "Demo_obj1"; 100 } , previously referencing obj1 no longer refers to either of your two obj1 or obj2 variables, but the instance is still stored (you didn’t touch it!) In the list and is reachable via objList[0] .

Now you understand why you get the behavior you see?

+3
source share

All Articles