Original address: http://www.c-sharpcorner.com/UploadFile/rmcochran/chsarp_memory401152006094206AM/chsarp_memory4.aspx
A Copy Is Not A Copy
To clarify this issue, let's check what happens in two scenarios: a value type in the heap and a reference type in the heap.Let's look at the value type first.Look at the classes and struct s below.We have a Dude class that contains a Name element and two Shoe s (s).At the same time, add a CopyDude() method to make new Dudes more convenient.
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 is a reference type, but Shoe is a structure, a value type, and the stack reacts like this:
When we implement 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(); Ted.Name = "Ted"; Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red"; Console.WriteLine(Bill.ToString()); Console.WriteLine(Ted.ToString()); }
We got the 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.
What happens if we change Shoe to a reference type?That's the problem.We'll change Shoe to a reference type (class):
public class Shoe{ public string Color; }
Let's execute the Main() method again, and what we get is:
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
Notice the red part, which is obviously an error.This is what we get in the heap:
Because we now use Shoe as a reference type instead of a value type, when we copy the content of a reference type, we just copy the pointer.We have to do some extra work to make our reference types look more like value types.
Fortunately, we have an interface that can help us solve this problem: ICloneable.This interface is a contract that all Dudes abide by and defines how an application type is replicated to prevent our shoe sharing errors.All classes need to implement the ICloneable interface to be clone d, and our Shoe is no exception.
ICloneable contains one method: Clone():
public object Clone() { }
We will change the Shoe class to the following:
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 just have a new Shoe, clone all the reference types and copy all the value types, and return a new object.As you may have noticed, string already implements ICloneable, so we can call Color.Clone() directly.Since the Clone() method returns the reference type, we must reset the reference type (retype the reference) before we can set the color of the show.
Next, in our CopyDude() method, we need to clone 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 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()); }
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
That's what we need:
Wrapping Things Up
As a general case, we always want to clone reference types and copy value types.(This will reduce the number of aspirins you have to buy to prevent headaches when you debug these errors)
To reduce headaches, let's go one step further and organize the Dude class so that it wants ICloneable 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 }
We also need to modify the Main() method to use Dude.Clone():
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()); }
Our final output 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.
Everything is OK.
Some interesting things to note are that System.String's assignment (=operator) actually clones a string, so you don't have to worry about duplicate references.However, you need to be careful to prevent memory expansion.If you look at the icon above, because string is a reference type, it needs to point to another object in the heap, but for simplicity, it appears as a value type.
Summary:
Usually, if we have plans to copy our objects, we need to implement ICloneable.This makes our reference types behave a little like value types.As you can see, it is important to note which type of variable we are working with because the value type and reference type are different when allocating memory.
To be continued...
Reprinted at: https://www.cnblogs.com/tian2010/archive/2012/05/17/2507087.html