introduction
There are two type variables in C#, one is the value type variable, and the other is the reference type variable. For value type variables, deep copy and first copy are implemented through the assignment operation symbol (=). The effect is consistent, and the field of value type in the object is copied into a new object. This is easy to understand. This article focuses on the copying mechanism and implementation of cited type variables.
There are two types of copy operations that refer to type objects in C#:
•Shallow copy (shadow cloning/shallow copy): Only copy the object's value type field, the object's reference type still belongs to the original reference.
•Deep copy (deep cloning): Not only copy the value type field of the object, but also copy the objects in the original object. That is to say, it is completely generated by the new object.
Difference between shallow copy and deep copy: shallow copy refers to copying a field of numerical type in an object into a new object, while a reference field in an object refers to copying one of its references to the target object.
Note: The string type is a bit special, and for shallow copies, class value type objects are processed.
Implementation of shallow copy
1. Implementation using the Object class MemberwiseClone
MemberwiseClone: Creates a shallow copy of the current Object.
The MemberwiseClone method creates a shallow copy by creating a new object and then copying the non-static fields of the current object to the new object. If the field is of value type, bit-by-bit copying is performed on the field. If the field is a reference type, the referenced object is copied but not the referenced object; therefore, the original object and its copy refer to the same object.
The code implementation is as follows:
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public object Clone()
{
return ();
}
}
public class Name
{
public Name(string frisName,string lastName)
{
FristName = frisName;
LastName = lastName;
}
public string FristName { get; set; }
public string LastName { get; set; }
}
2. Assignment operation (=) VS is implemented using the Object class MemberwiseClone
We have a misunderstanding about variables of reference types, which is that assignment operations are just a shallow copy of one. In fact, it is not the case, and there is a difference between the two.
1. Shallow copy (shallow copy) bit by bit copying of the value type field in the reference type object. The assignment operator simply assigns the reference of the source object to the destination object, and both reference the same object.
2. Changes in the value type field of the object after the shallow copy will not be reflected to the source object, while changes in the value type field of the object after the assignment operation will be reflected to the source object.
The code implementation is as follows:
public class Person
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
}
public class Name
{
public Name(string frisName,string lastName)
{
FristName = frisName;
LastName = lastName;
}
public string FristName { get; set; }
public string LastName { get; set; }
}
Deep copy implementation
Compared with shallow copy, it means that according to the source object as a prototype, a new object is created, and all fields of the current object are copied bit by bit and supports recursion, whether it is a value type or a reference type, whether it is a static field or a non-static field.
In C#, we have three ways to achieve deep copy
1. Implement ICloneable interface and custom copy function.
ICloneable interface, supports cloning, that is, create new instances of classes with the same value as existing instances.
The ICloneable interface contains a member Clone that supports clones other than those provided by MemberwiseClone. Clone can be implemented as a deep copy or as a shallow copy. In deep replicas, all objects are duplicate; in shallow replicas, only the top-level objects are duplicate, and objects below the top-level contain references. The result cloning must have the same type as the original instance or is a compatible type of the original instance.
The code implementation is as follows:
public class Person:ICloneable
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public object Clone()
{
Person tem = new Person();
= ;
= ;
= new Name(, );
return tem;
}
}
public class Name
{
public Name(string frisName, string lastName)
{
FristName = frisName;
LastName = lastName;
}
public string FristName { get; set; }
public string LastName { get; set; }
}
As you can see, the Person class inherits the interface ICloneable and manually implements its Clone method. This is a simple class. Just imagine, if your class has thousands of reference type members (of course it is too exaggerated, there are still dozens of them), is this a terrifying labor?
2. Serialization/deserialization class implementation
I don't know if you have noticed the DataSet object, for the two methods it provides:
Method, copy the structure of the DataSet, including all DataTable architectures, relationships, and constraints. Do not copy any data.
The new DataSet has the same architecture as the current DataSet, but does not contain any data. Note If subclasses of these classes have been created, the replicas will also belong to the same subclass.
Method copy the structure and data of the DataSet.
The new DataSet has the same structure (table schema, relationships, and constraints) and data as the DataSet. Note that if subclasses of these classes have been created, the copy will also belong to the same subclass.
It seems that it is neither a shallow copy nor a deep copy, aren't you very disappointed? But isn’t the combination of the two the deep copy we want? Take a look at the implementation of DataSet and pay attention to the serialization interface: ISerializable
Serialization is the process of converting an object or object graph into a linear sequence of bytes for storage or transfer to another location. Deserialization is the process of accepting stored information and recreating objects with it.
Through the ISerializable interface, classes can perform their own serialization behavior.
Is the process of converting to a linear byte sequence and recreating the object using it very similar to the semantic "bit-by-bit copy" of our deep copy?
The code implementation is as follows:
[Serializable]
public class Person : ICloneable
{
public int Age { get; set; }
public string Address { get; set; }
public Name Name { get; set; }
public object Clone()
{
using (MemoryStream ms = new MemoryStream(1000))
{
object CloneObject;
BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext());
(ms, this);
(0, );
// Deserialize to another object (that is, create a deep-table copy of the original object)
CloneObject = (ms);
// Close the stream
();
return CloneObject;
}
}
}
[Serializable]
public class Name
{
public Name(string frisName, string lastName)
{
FristName = frisName;
LastName = lastName;
}
public string FristName { get; set; }
public string LastName { get; set; }
}
}
Note: Deep copy is achieved through serialization and deserialization, and its field types must be marked as serializable types, both adding attributes [Serializable].
3. Implemented through reflection
Through serialization/deserialization, we can achieve deep copy smoothly, but it involves IO operations. In the managed environment, IO operations consume more resources. Can there be a more elegant solution? CreateInstance, yes, utilizes reflection properties. You can refer to this blog: /?p=70 The Attribute of the reflection class, use New class to get it out (a bit like getting the architecture first), and then use PropertyInfo's SetValue and GetValue methods to traverse the value filling method.
The code implementation is as follows:
public class Person
{
private List<Person> _friends = new List<Person>();
public string Firstname { get; set; }
public string Lastname { get; set; }
[Cloneable()]
[Cloneable(, "Friends")]
public List<Person> Friends { get { return _friends; } }
[Cloneable()]
public PersonManager Manager { get; set; }
}
Why do you need to design deep and shallow copies in C#?
I have never found a suitable answer to this, I hope someone will discuss it!Click to download the code