Recommendation 14: Properly implement shallow and deep copies
The technique of creating a copy of an object is called a copy (also called a clone). We divide the copy into shallow copy and deep copy.
- Shallow copy copies all the fields in the object to the new object (copy). Where the value of the value Type field is copied to the copy, the modifications in the copy do not affect the value corresponding to the source object. While a reference type's field is copied to a reference of a reference type, instead of a referenced object, modifying the field value of the reference type in the replica affects the source object itself.
- Deep copy also, copy all the fields in the object to the new object. However, whether the object's value Type field, or reference Type field, is recreated and assigned a value, modifications to the copy do not affect the source object itself.
Whether it is a shallow copy or a deep copy, Microsoft recommends explicitly telling the caller in a way that the type inherits the ICloneable interface: The type can be copied. Of course, the ICloneable interface only provides a method that is declared as clone, and we can implement a shallow copy or deep copy within the Clone method as required. The implementation code for a simple shallow copy is as follows:
classemployee:icloneable { Public stringIdcode {Get;Set; } Public intAge {Get;Set; } PublicDepartment Department {Get;Set; } #regionICloneable Members Public ObjectClone () {return This. MemberwiseClone (); } #endregion } classDepartment { Public stringName {Get;Set; } Public Override stringToString () {return This. Name; } }
The caller code looks like this:
Employee Mike =NewEmployee () {Idcode ="NB123", age = -, Department=NewDepartment () {Name ="Dep1" } }; Employee Rose= Mike. Clone () asEmployee; Console.WriteLine (Rose. Idcode); Console.WriteLine (Rose. Age); Console.WriteLine (Rose. Department); Console.WriteLine ("start changing Mike's value:"); Mike. Idcode="NB456"; Mike. Age= -; Mike. Department.name="DEP2"; Console.WriteLine (Rose. Idcode); Console.WriteLine (Rose. Age); Console.WriteLine (Rose. Department);
The output is:
NB123 - DEP1 starts to change Mike's value: NB123
Note that the Idcode property of the employee is a string type. The string type is theoretically a reference type, but because of the specificity of the reference type, whether implemented or semantic, the Object.memberwiseclone method still creates a copy of it. That is, in a shallow copy process, we should treat the string as a value type. The Department property of an employee is a reference type, so if you change the value in the source object Mike, the value in the copy rose changes along with it. There are many ways to implement a deep copy of an employee, and the simplest way is to manually assign the field one by one. However, this method is prone to error, that is, if the type of the field changes or increase or decrease, then the copy method will also have a corresponding change, so it is recommended to use the form of serialization for deep copy. A simple implementation code for the employee deep copy is as follows:
classemployee:icloneable { Public stringIdcode {Get;Set; } Public intAge {Get;Set; } PublicDepartment Department {Get;Set; } #regionICloneable Members Public ObjectClone () {using(Stream Objectstream =NewMemoryStream ()) {IFormatter Formatter=NewBinaryFormatter (); Formatter. Serialize (Objectstream, This); Objectstream.seek (0, Seekorigin.begin); returnFormatter. Deserialize (Objectstream) asEmployee; } } #endregion }
Using the caller code in a shallow copy, the output is:
NB123 - DEP1 starts to change Mike's value: NB123
It can be found that changing the value of Mike again does not affect the value of the replica rose. Since interface ICloneable has only one ambiguous clone method, if you want to implement both deep and shallow copies in a class, we can only implement two additional methods, declared as Deepclone and shallow. The final version of the employee should look like the following form:
[Serializable]classemployee:icloneable { Public stringIdcode {Get;Set; } Public intAge {Get;Set; } PublicDepartment Department {Get;Set; } #regionICloneable Members Public ObjectClone () {return This. MemberwiseClone (); } #endregion PublicEmployee Deepclone () {using(Stream Objectstream =NewMemoryStream ()) {IFormatter Formatter=NewBinaryFormatter (); Formatter. Serialize (Objectstream, This); Objectstream.seek (0, Seekorigin.begin); returnFormatter. Deserialize (Objectstream) asEmployee; } } PublicEmployee Shallowclone () {returnClone () asEmployee; } }
Turn from: 157 recommendations for writing high-quality code to improve C # programs Minjia
157 recommendations for writing high-quality code to improve C # programs--recommendation 14: correctly implement shallow and deep copies