Duplicate of reference type in heap and use the clone interface ICloneable to fix it
Navigation
In-depth illustration C # Heap and Stack C # Heap (ing) VS Stack (ing) Section 1 Understanding Heap and Stack depth diagram C # Heap and Stack C # Heap (ing) VS Stack (ing) Section 2 basic working principles of Stack a simple illustration C # Stack and Stack C # Heap (ing) VS Stack (ing) Section 3 Stack and Stack, in-depth illustration of value type and reference type C # Heap and Stack C # Heap (ing) VS Stack (ing) section 4 influence of parameter passing on the Stack 1 in-depth illustration C # Heap and Stack C # Heap (ing) VS Stack (ing) section 4 influence of parameter transfer on Stack 2 in-depth illustration C # Heap and Stack C # Heap (ing) VS Stack (ing) Section 5 Reference Type replication problem and use the clone interface ICloneable to fix
Preface
Although we do not have to consider internal management and garbage collection (GC) in. Net Framework, we always need to understand memory management and garbage collection (GC) to optimize application performance ). In addition, understanding memory management helps us understand how every variable defined in every program works.
This section describes the problems that may occur when the referenced type variables are stored in the heap. It also describes how to use the clone interface ICloneable to fix this problem.
Copying is not just copying
To clarify this issue, let's test the differences between storing value type variables and referencing type variables in the heap.
Value Type Test
First, let's take a look at the value type. The following is a class and a structure type (Value Type). The Dude class contains a Name element and two Shoe elements. We have a CopyDude () method to copy and generate a new Dude.
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.");
}
}
The Dude class is a complex type, because the value type structure Shoe is its member, and they will all be stored in the heap.
When we execute 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 ());
}
We got the desired 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.
What if we replace Shoe with a reference type?
Reference type test
When we changed Shoe to a reference type, the problem arises.
public class Shoe {
public string Color;
}
Executing the same Main () method above, the result has changed 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
This is not what we expected. Obviously, something went wrong! See the following diagram:
Because now Shoe is a reference type instead of a value type, when we copy, we only copy the pointer, and we don't copy the object that the pointer really corresponds to. This requires us to do some additional work to make the reference type Shoe work like a value type. Fortunately, we have an interface that can help us achieve it: ICloneable. When the Dude class implements it, we will declare a Clone () method to generate a new Dude replication class. (Foreign translation: the copied class and its members do not have any overlap with the original class, which is what we call deep copy) See the following code:
ICloneable consists of one method: Clone ()
public object Clone ()
{
}
Here's how we'll implement it in the Shoe class:
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
}
In the Clone () method, we create a new shoe, clone all reference type variables, copy all value type variables, and finally return the new object shoe. Some existing classes have implemented ICloneable, we can use it directly, such as String. Therefore, we directly use Color.Clone (). Because Clone () returns an object, we need to perform a type conversion. Next, in the CopyDude () method, we use clone Clone () instead of copying:
public Dude CopyDude ()
{
Dude newPerson = new Dude ();
newPerson.Name = Name;
newPerson.LeftShoe = LeftShoe.Clone () as Shoe;
newPerson.RightShoe = RightShoe.Clone () as Shoe;
return newPerson;
}
Execute the main method Main () again:
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 got the desired 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
The following is an illustration:
Organize our code
In practice, we want to clone reference types and copy value types. This will allow you to avoid many imperceptible errors, as demonstrated above. This kind of error is sometimes not easy to debug, it will make you a headache.
Therefore, in order to reduce headaches, let us further clean up the above code. We let the Dude class implement IConeable 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 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
}
Use Dude.Clone () in the main method Main ():
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 ());
}
Finally get the desired 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.
Special reference type String
What is interesting in C # is that when System.String uses the operator "=", it is actually cloned (deep copy). You don't have to worry that you are just manipulating a pointer, it will create a new object in memory. However, you must pay attention to the problem of memory occupation (Foreign translation: for example, why we use StringBuilder instead of String + String + String + String ...). If we go back and look at the above diagram, you will find that the Stirng type is not a needle pointing to another memory object in the figure, but for simplicity, it is demonstrated as a value type.
to sum up
In actual work, when we need to copy a reference type variable, we better let it implement the ICloneable interface. This allows reference types to mimic the use of value types, thereby preventing accidental errors. You can see that it is very important to be careful about different types, because the allocation of value types and reference types in memory is different.
Translation: http://www.c-sharpcorner.com/UploadFile/rmcochran/chsarp_memory401152006094206AM/chsarp_memory4.aspx