Although we do not need to worry about memory management and garbage collection in. NET Framework, we should still understand them to optimize our applications.
At the same time, we also need to have some basic knowledge of memory management mechanisms, which can help to explain the behavior of variables in our daily program writing.
In this article, we will discuss the problem caused by referencing variables in the heap and how to use the icloneable interface to solve the problem.
You need to review the basic stack, value type and reference type. Please go to part 1 and part 2
* The copy is not a real copy.
To clarify the problem clearly, let's compare what happens when the value type and reference type exist in the heap.
First, let's take a look at the value type, the following class and structure.
Here, there is a dude class. Its members include a Name field of the string type and two shoe fields -- rightshoe, leftshoe,
Another copydude () method can easily generate a new dude instance.
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.");}}
Dude is a reference type, and because the two fields in the shoe structure are members of the dude class, they are all placed on the stack.
When we execute the following methods:
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 (); // note Ted below. name = "Ted"; Ted. leftshoe. color = Ted. rightshoe. color = "red"; console. writeline (Bill. tostring (); console. writeline (Ted. tostring ());}
We have expected results:
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.
(The above object is being copied)
What happens if we replace shoe with the reference type? This is the problem.
Let's change shoe to the reference type: (the previous example is structured, and now it is changed to a class)
public class Shoe{public string Color;}
Run the command in the same main () method as above. Then let's take a look at our results:
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
(It indicates that the reference is just copying, and the reference object is not copying)
We can see that the red shoes have been put on another person's foot (Bill), which is obviously wrong. Do you want to know why? Let's take a look at the heap.
Because the shoe we are currently using is of the reference type rather than the value type, when the content of the reference type is copied, only the pointer of this type is actually copied (the actual object is not copied ),
We need to do some extra work to make our reference type usable like the value type.
Fortunately, the. NET Framework already has an iclonealbe interface (System. icloneable) To help us solve the problem.
This interface can be used to specify that all dude classes must comply with and define how the reference types should be copied to avoid the "shared shoes" problem.
All classes to be cloned must use the icloneable interface, including the shoe class.
System. iclonealbe has only one method definition: Clone ()
public object Clone(){}
We should implement the following in the shoe class:
public class Shoe : ICloneable{public string Color;#region ICloneable Memberspublic object Clone(){Shoe newShoe = new Shoe();newShoe.Color = Color.Clone() as string;return newShoe;}#endregion}
In method clone (), we create a new shoe object, clone all reference types, copy all value types, and return this new object.
You may notice that the string class has implemented the icloneable interface, so we can directly call the color. Clone () method.
Because the clone () method returns an object reference, we need to set the shoe color beforeReconstructionThis reference.
Next, in our copydude () method, we need to clone the shoes instead of copying them:
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, when we execute the main () function:
Public static void main () {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 (); // here Ted. name = "Ted"; Ted. leftshoe. color = Ted. rightshoe. color = "red"; console. writeline (Bill. tostring (); console. writeline (Ted. tostring ());}
What we get is:
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.
In general, we should "clone" the reference type and "copy" the value type. (In this way, when you debug the problems described above, the dose of aspirin you bought to control headaches will be reduced)
In the fierce reduction of headaches, we can further use the dude class to implement iclonealbe, instead of using 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 Memberspublic 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}
Then we change the dude. copydude () method in the main () method to dude. Clone ():
public static void Main(){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: 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.
Very good!
It is interesting to note that the operator ("=") assigned to the system. string class is actually cloning string objects, so you don't have to worry about reference copying.
Even so, you should pay attention to the memory expansion. If you look at the previous figures, you will find that the string type should be of the reference type, so it should be a pointer (this Pointer Points to another object in the heap ),
But for convenience, I will represent the string type as the value type in the figure (actually a pointer ), because the string object that is assigned with the "=" sign is automatically cloned.
Summary:
Generally, if we intend to use our object for copying, our class should implement iclonealbe, so that the reference type can emulate the value type.
From this we can see that it is very important to understand the type of the variables we use, because there is a difference between the value type and the allocation of reference type objects in the memory.
In the next section, we will see how we can reduce the "footprint" of code in the memory. We will talk about the long-awaited garbage collector (garbage collection ).
To be continued...