You must know that. Net objects have two types: Value Type and reference type. The variable of the Value Type object represents the object and has the "Copy-on-assignment" action. That is to say, the following discussion does not apply to value types.
On the other hand, variables of the reference type actually point to the memory on the stack. Therefore, if you create a variable of the reference type and allocate an existing object to it, you actually create another object pointing to the same memory on the stack. This article discusses the situation where a new copy of an object is created and saved in a variable!
Why clone?
I think it is very necessary to clone an object when it is set to an object at an expensive cost and a copy of the object is required to change the current state. The following is an example of what I just mentioned. Take the datatable class as an example. Creating a able includes the following operations: querying the database for obtaining the Architecture and Data, adding constraints, and setting the primary key. Then, when a new copy of the datatable is required, even if the architecture is greatly changed or a new row of records is added, it is wise to clone an existing object and then operate on it, instead of creating a new able, which requires more time and resources.
Cloning is also widely used in arrays and collections, which often requires a copy of an existing object multiple times.
Clone type
We divide the clone into two categories based on the clone degree: "deep" clone and "superficial" clone. The "superficial" clone is used to obtain a new instance. A copy of the instance is of the same type as the original object and contains a value type field. However, if the field is of the reference type, the reference will be copied instead of the referenced object. Therefore, both the reference of the original object and the reference of the cloned object point to the same object. On the other hand, the object's "deep" clone contains all copies of objects directly or indirectly referenced by the original object. The following is an example.
Object x references object A, and object a references object m. The "superficial" clone object y of object X also references object. The relative ratio is that the "deep" clone object y of object x directly references object B and indirectly references object N. Here, object B is a copy of object, object N is a copy of object m.
Clone
System. Object provides the protected method memberwiseclone, which can be used for "superficial" cloning. Because this method is marked as "protected", we can only access this method within the inheritance class or class.
. Net defines an iclonable interface, which must be implemented by a class that requires "deep" cloning instead of "superficial" cloning. We need to provide a good implementation method to achieve the clone method implementation function of this interface.
There are many ways to achieve "deep" cloning. One method is to serialize the object to the memory stream, and then deserialize it to a new object. We will use a binary formatter class or soap formatter class for deep serialization. Create a formatter that is deeply written as a serialized long article. The problem with this method is that the class and its members (the complete class table) must be marked as serializable; otherwise, an error occurs in formatter.
Reflection is another method that can achieve the same purpose. A good article by Amir Harel attracted me and he used this method to provide a good clone implementation. This article is very well discussed! Here is the link:
Http://www.codeproject.com/csharp/cloneimpl_class.asp
Any method discussed above requires that the object's member types support self-cloning to ensure that "deep" Cloning can be successful. That is to say, the object must be serializable, or each independent member must provide the Implementation of iclonable. If this is not the case, we cannot perform "deep" cloning on the object!
Summary
Cloning is a good method provided to programmers. However, we should know when to provide such a feature, and in some cases, strictly speaking, objects should not provide this feature. Taking the sqltransaction class as an example, cloning is not supported. This class represents a transaction in the SQL Server database. Cloning this object makes no sense, because we may not be able to understand the cloning of an active transaction in a database! Therefore, if you think that the status of the cloned object will cause logical conflicts in the application, you do not need to support cloning.
Sample Code:
Using system;
Using system. reflection;
Using system. collections;
Namespace amir_harel.cloning
{
/// <Summary>
/// <B> baseobject </B> class is an abstract class for you to derive from. <br>
/// Every class that will be dirived from this class will support the <B> clone </B> method automaticly. <br>
/// The class implements the interface <I> icloneable </I> and there for every object that will be derived <br>
/// From this object will support the <I> icloneable </I> interface as well.
/// </Summary>
Public abstract class baseobject: icloneable
{
/// <Summary>
/// Clone the object, and returning a reference to a cloned object.
/// </Summary>
/// <Returns> reference to the new cloned object. </returns>
Public object clone ()
{
// First we create an instance of this specific type.
Object newobject = activator. createinstance (this. GetType ());
// We get the array of fields for the new type instance.
Fieldinfo [] fields = newobject. GetType (). getfields ();
Int I = 0;
Foreach (fieldinfo fi in this. GetType (). getfields ())
{
// We query if the fiels support the icloneable interface.
Type iclonetype = Fi. fieldtype. getinterface ("icloneable", true );
If (iclonetype! = NULL)
{
// Getting the icloneable interface from the object.
Icloneable iclone = (icloneable) Fi. getvalue (this );
// We use the clone method to set the new value to the field.
Fields [I]. setvalue (newobject, iclone. Clone ());
}
Else
{
// If the field doesn' t support the icloneable interface then just set it.
Fields [I]. setvalue (newobject, Fi. getvalue (this ));
}
// Now we check if the object supports the ienumerable interface, so if it does
// We need to enumerate all its items and check if they support the icloneable interface.
Type ienumerabletype = Fi. fieldtype. getinterface ("ienumerable", true );
If (ienumerabletype! = NULL)
{
// Get the ienumerable interface from the field.
Ienumerable ienum = (ienumerable) Fi. getvalue (this );
// This version supports the ilist and the idictionary interfaces to iterate
// On collections.
Type ilisttype = Fields [I]. fieldtype. getinterface ("ilist", true );
Type idictype = Fields [I]. fieldtype. getinterface ("idictionary", true );
Int J = 0;
If (ilisttype! = NULL)
{
// Getting the ilist interface.
Ilist list = (ilist) fields [I]. getvalue (newobject );
Foreach (Object OBJ in ienum)
{
// Checking to see if the current item supports the icloneable interface.
Iclonetype = obj. GetType (). getinterface ("icloneable", true );
If (iclonetype! = NULL)
{
// If it does support the icloneable interface, we use it to set the clone
// The object in the list.
Icloneable clone = (icloneable) OBJ;
List [J] = clone. Clone ();
}
// Note: if the item in the list is not support the icloneable interface then
// In the cloned list this item will be the same item as in the original list
// (As long as this type is a reference type ).
J ++;
}
}
Else if (idictype! = NULL)
{
// Getting the dictionary interface.
Idictionary DIC = (idictionary) fields [I]. getvalue (newobject );
J = 0;
Foreach (dictionaryentry de in ienum)
{
// Checking to see if the item supports the icloneable interface.
Iclonetype = de. value. GetType (). getinterface ("icloneable", true );
If (iclonetype! = NULL)
{
Icloneable clone = (icloneable) De. value;
Dic [de. Key] = clone. Clone ();
}
J ++;
}
}
}
I ++;
}
Return newobject;
}
}
}
# C # column