C # heap VS stack (Part Three)
In Part Two, the first article in this series describes the differences between the value type and the reference type when passing parameters, this article will discuss how to apply the ICloneable interface to fix the problems caused by the use of variables on the stack. This is the third part of the series. Note: I have limited my understanding in English and technical experience. If you have any mistakes in this article, please kindly advise. Copying is not as simple as copying. To better express this problem, we will examine the value types on the stack and the reference types on the stack. First, let's look at the value type. Following the following classes and struct, we have a Dude class containing Name and two Shoe fields. We have a CopyDude method to help us generate a new Dude (playboy ). Public struct Shoe {public string Color;} public class Dude {public string Name; public Shoe RightShoe; public Shoe LeftShoe; public Dude CopyDude () {Dude newPerson = new Dude (); newPerson. name = Name; newPerson. leftShoe = LeftShoe; newPerson. rightShoe = RightShoe; return newPerson;} public override string ToString () {return (Name + ": Dude !, I have a "+ RightShoe. color + "shoe on my right foot, and a" + LeftShoe. color + "on my left foot. ") ;}} our Dude class is a reference type (originally the variable type, which has been corrected by the author) and the Shoe struct is a member of the class, all of which are on the stack. Note: whether the value type is on the stack or on the stack depends entirely on its place of life. When we run the following method: public static void Main () {Class1 pgm = new Class1 (); Dude Bill = new Dude (); Bill. name = "Bill"; Bill. leftShoe = new Shoe (); Bill. rightShoe = new Shoe (); Bill. leftShoe. color = Bill. rightShoe. color = "Blue"; Dude Ted = Bill. copyDude (); Ted. name = "Ted"; Ted. leftShoe. color = Ted. rightShoe. color = "Red"; Console. writeLine (Bill. toString (); Console. writeLine (Ted. toString ();} The result is as follows: Bill: Dude !, I have a Blue shoe on my right foot, and a Blue on my left foot. Ted: Dude !, I have a Red shoe on my right foot, and a Red on my left foot. What if we change Shoe to the reference type? The change is as follows: public class Shoe {public string Color;} run the code again. The result is as follows: Bill: Dude !, I have a Red shoe on my right foot, and a Red on my left foot Ted: Dude !, I have a Red shoe on my right foot, and a Red on my left foot Red shot on another person, which is obviously wrong. Can you see how this happened? Is the memory reference example: because now we use the Shoe reference type to replace the value type, and when copying the reference type content, we only copy the pointer (not the object actually pointed to by the pointer ), we must do some extra work to make Shoe of the reference type more consistent with the behavior of the value type. Note: In the above example, when Shoe is of the value type, a completely independent structure Shoe object has been generated along with the Dude constructor, So Bill is a blue Shoe, ted is a red Shoe. When Shoe is of the reference type, Shoe is initialized only once. Therefore, when Shoe is used, the only Shoe content for initialization is actually changed, as a result, all of them are red shoes. The following describes how to use the deep copy function to copy pointer of the reference type. Fortunately, we have an ICloneable interface to help solve the problem. This interface is a basic contract. All Dudes comply with this contract and stipulate how to copy in order to avoid the Shoe Sharing problem. All the classes to be copied should use the ICloneable interface, including the Shoe class. ICloneable includes a method: Clone (). Below we will implement this interface: public class Shoe: ICloneable {public string Color; # region ICloneable Members public object Clone () {Shoe newShoe = new Shoe (); newShoe. color = Color. clone () as string; return newShoe;} # endregion} inside Clone, we only create a New Shoe object, copy all reference types and copy value types, then a new object is returned. You may notice that the String class has implemented the ICloneable interface, so we can call the Color. Clone method. Because Clone returns an object reference, we must convert the type display to the Shoe type before setting the Shoe color. Note: The String type is a special reference type, which is similar to the value type, because the String cannot be changed. If it is changed, a new object is generated. For details, refer to here. Next, in our CopyDude method, we need to clone Shoes instead of copy. Public Dude CopyDude () {Dude newPerson = new Dude (); newPerson. name = Name; newPerson. leftShoe = LeftShoe. clone () as Shoe; newPerson. rightShoe = RightShoe. clone () as Shoe; return newPerson;} Now we run the Main method: public static void Main () {Class1 pgm = new Class1 (); Dude Bill = new Dude (); bill. name = "Bill"; Bill. leftShoe = new Shoe (); Bill. rightShoe = new Shoe (); Bill. leftShoe. color = Bill. rightShoe. color = "Blue"; Dude Ted = Bill. copyDude (); Ted. name = "Ted"; Ted. leftShoe. color = Ted. rightShoe. color = "Red"; Console. writeLine (Bill. toString (); Console. writeLine (Ted. toString ();} we get the following result: Bill: Dude !, I have a Blue shoe on my right foot, and a Blue on my left foot Ted: Dude !, I have a Red shoe on my right foot, and a Red on my left foot this is what we want. Wrap things as an exercise and we want to always clone the reference type and copy the value type. (This will reduce the amount of aspirin you have purchased for headaches when debugging programs are incorrect.) So, when the headache is reduced, let's go further and let's sort out the Dude class to implement the ICloneable interface method instead of the CopyDude method. Public class Dude: ICloneable {public string Name; public Shoe RightShoe; public Shoe LeftShoe; public override string ToString () {return (Name + ": Dude !, I have a "+ RightShoe. color + "shoe on my right foot, and a" + LeftShoe. color + "on my left foot. ") ;}# region ICloneable Members public object Clone () {Dude newPerson = new Dude (); newPerson. name = Name. clone () as string; newPerson. leftShoe = LeftShoe. clone () as Shoe; newPerson. rightShoe = RightShoe. clone () as Shoe; return newPerson;} # endregion} all we have to do is use Dude. clone changes the content in the Main method. Public static void Main () {Class1 pgm = new Class1 (); Dude Bill = new Dude (); Bill. name = "Bill"; Bill. leftShoe = new Shoe (); Bill. rightShoe = new Shoe (); Bill. leftShoe. color = Bill. rightShoe. color = "Blue"; Dude Ted = Bill. clone () as Dude; Ted. name = "Ted"; Ted. leftShoe. color = Ted. rightShoe. color = "Red"; Console. writeLine (Bill. toString (); Console. writeLine (Ted. toString ();} the final result is: Bill: Dud E !, I have a Blue shoe on my right foot, and a Blue on my left foot. Ted: Dude !, I have a Red shoe on my right foot, and a Red on my left foot. So everything is normal. Note that the System. String operator "=" really clones the String, so you don't have to worry about repeated references. However, you must pay attention to the memory expansion. If you look back at the illustration, the string is of the reference type, and it should actually be a pointer to the heap, but for simplicity, its function is similar to the value type. Summary as an exercise, if we plan to copy objects every time, we should implement the ICloneable interface. This will make sure that our reference type is a bit like imitating the behavior of the value type. As you can see, it is important to record the variables we are processing, because the difference between the reference type and value type in memory creation. In this next article, we will examine a way to reduce the memory footprint. 1. to copy a reference type, you must pay attention to whether it is a deep copy or a simple pointer copy. 2. The System. String type is a special reference type. The actual effect is similar to the value type. 3. The ICloneable interface should be implemented for the reference type to achieve deep copy, that is, object copy rather than pointer copy.