A lot of times we copy an object instance A to instance B, and when we do something with instance B, we modify instance B to make sure that the modification to B does not affect the normal use of a, you need to use a deep copy.
I found some deep copy methods on the Internet and wrote several sets of examples to test these methods.
My operating system version is Win7 flagship, the. NET Framework version is 4.5
Test program
I built a C # form application (Winform) whose main window formmain the load function as follows:
private void Formmain_load (object sender, EventArgs e)
{
//test 1: Deep copy custom class
try
{
Console.WriteLine ("= = = Deep copy of the custom class = =");
TestClass test1 = new TestClass ();
test1.a = ten;
test1.b = "Hello world!";
test1.c = new string[] {"x", "Y", "Z"};
TestClass test2 = new TestClass ();
test2.a = one;
test2.b = "Hello world2!";
test2.c = new string[] {"I", "J", "K"};
test1.d = test2;
Console.WriteLine ("---test1_start---");
Console.WriteLine (test1);
Console.WriteLine ("---test1_end---");
TestClass test3 = (testclass) datamanhelper.deepcopyobject (test1);
Console.WriteLine ("---test3_start---");
Console.WriteLine (TEST3);
Console.WriteLine ("---test3_end---");
}
catch (Exception ex)
{
Console.WriteLine (ex. ToString ());
}
Test 2: Deep copy serializable custom classes
Try
{
Console.WriteLine ("= = = = (= = =") (custom class = = "deep copy serializable)
Testclasswiths test1 = new Testclasswiths ();
test1.a = ten;
test1.b = "Hello world!";
test1.c = new string[] {"x", "Y", "Z"};
Testclasswiths test2 = new Testclasswiths ();
test2.a = one;
test2.b = "Hello world2!";
test2.c = new string[] {"I", "J", "K"};
test1.d = test2;
Console.WriteLine ("---test1_start---");
Console.WriteLine (test1);
Console.WriteLine ("---test1_end---");
Testclasswiths test3 = (testclasswiths) datamanhelper.deepcopyobject (test1);
Console.WriteLine ("---test3_start---");
Console.WriteLine (TEST3);
Console.WriteLine ("---test3_end---");
}
catch (Exception ex)
{
Console.WriteLine (ex. ToString ());
}
Test 3: Deep copy DataTable
try {Console.WriteLine ("= = = Deep copy DataTable = = =");
DataTable Dtkirov = new DataTable ("TestTable");
DTKIROV.COLUMNS.ADD ("Col1");
DTKIROV.COLUMNS.ADD ("Col2");
DTKIROV.COLUMNS.ADD ("Col3");
DTKIROV.ROWS.ADD ("1-1", "1-2", "1-3");
DTKIROV.ROWS.ADD ("2-1", "2-2", "2-3");
DTKIROV.ROWS.ADD ("3-1", "3-2", "3-3");
Console.WriteLine ("= = = = = ="); for (int i = 0; i < DtKirov.Columns.Count i++) {Console.Write (dtkirov.columns[i).
ColumnName + "T");
} Console.WriteLine ("\ n-----------------"); for (int i = 0; i < DtKirov.Columns.Count. i++) {for (int j = 0; J < DtKirov.Rows.Count; J +) {Console . Write (DTKIROV.ROWS[I][J).
ToString () + "T");
} Console.WriteLine ();
} Console.WriteLine ();
DataTable dtdreadnought = (DataTable) Datamanhelper.deepcopyobject (Dtkirov);
Console.WriteLine ("= = = = after the copy = = ="); for (int i = 0; i < DtDreadNought.Columns.Count i++) {Console.Write (dtdreadnought.columns[i).
ColumnName + "T"); } CONSOLE.WRIteline ("\ n-----------------");
for (int i = 0; i < DtDreadNought.Columns.Count. i++) {for (int j = 0; J < DtDreadNought.Rows.Count; J +) { Console.Write (Dtdreadnought.rows[i][j].
ToString () + "T");
} Console.WriteLine ();
} Console.WriteLine (); The catch (Exception ex) {Console.WriteLine (ex).
ToString ());
}
Test 4: Deep copy TextBox
Try
{
Console.WriteLine ("= = = Deep copy TextBox = = =");
Txttest.text = "1234";
Console.WriteLine ("Before copying:" + txttest.text);
TextBox txttmp = new TextBox ();
Txttmp = (TextBox) datamanhelper.deepcopyobject (txttest);
Console.WriteLine ("After copy:" + Txttmp.text);
}
catch (Exception ex)
{
Console.WriteLine (ex. ToString ());
}
Test 5: Deep copy DataGridView
Try
{
Console.WriteLine ("= = = Deep copy DataGridView = =)";
DataGridView dgvtmp = new DataGridView ();
Dgvtmp = (DataGridView) datamanhelper.deepcopyobject (dgvtest);
}
catch (Exception ex)
{
Console.WriteLine (ex. ToString ());
}
Where Txttest is a test textbox,dgvtmp is a test datagridview,testclass is a custom class, Testclasswiths is a serializable class with TestClass attributes added , their specific implementation is as follows:
Using System;
Using System.Collections.Generic;
Using System.Linq;
Using System.Text;
Namespace Datacopytest {public class TestClass {public int A;
public string B;
Public string[] C;
public TestClass D;
public override string ToString () {string s = "A:" + A + "\ n";
if (b!= null) {s + = "B:" + B + "\ n"; } if (c!= null) {foreach (String tmps in C) {if (!string).
Isnullorwhitespace (Tmps)) {s + = "C:" + tmps + "\ n";
}} if (d!= null) {s + = d.tostring ();
return s;
}///support serialization of TestClass [Serializable] public class Testclasswiths {public int A;
public string B;
Public string[] C;
public Testclasswiths D;
public override string ToString () {string s = "A:" + A + "\ n";
if (b!= null) {s + = "B:" + B + "\ n"; } if (c!= null) {foreach (String tmps in C) {if (!string).
Isnullorwhitespace (Tmps)) {s + = "C:" + tmps + "\ n";
} } if (d!= null) {s + = d.tostring ();
return s;
}
}
}
I used each of these five class instances for deep copy tests on each of the deep copy methods, and the five classes were characterized as follows:
I, deep copy test for custom class TestClass
II, a deep copy test of the custom class Testclasswiths, Testclasswiths is a TestClass class that adds the serializable attribute
III. Deep copy test for DataTable
IV, a deep copy test of the control textbox
V, deep Copy test for control DataGridView
We test by implementing the method Datamanhelper.deepcopyobject
Test deep Copy Method 1
Using binary streams for serialization and deserialization of deep copy objects
public static object Deepcopyobject (Object obj)
{
BinaryFormatter Formatter = new BinaryFormatter (NULL,
New StreamingContext (Streamingcontextstates.clone));
MemoryStream stream = new MemoryStream ();
Formatter.serialize (stream, obj);
Stream. Position = 0;
Object clonedobj = Formatter.deserialize (stream);
Stream. Close ();
return clonedobj;
}
The test results for five scenarios are:
I, triggering an exception serializationexception because the class does not support serialization
The first chance exception for the "System.Runtime.Serialization.SerializationException" type occurs in mscorlib.dll
System.Runtime.Serialization.SerializationException: Assembly "Datacopytest, version=1.0.0.0, Culture=neutral, The type "Datacopytest.testclass" in Publickeytoken=null is not marked as serializable.
In System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers (RuntimeType type)
In System.Runtime.Serialization.FormatterServices.GetSerializableMembers (type type, StreamingContext context)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo ()
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, Serializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, S "DataCopyTest.vshost.exe" (Managed (v4.0.30319)): Loaded "C:\Windows\Microsoft.Net\assembly\GAC_ Msil\system.numerics\v4.0_4.0.0.0__b77a5c561934e089\system.numerics.dll "
Erializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize (Object graph, header[] inheaders, __ BinaryWriter Serwriter, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph, header[] headers, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 24
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 37
II, can be normal replication (√)
III, can be normal replication (√)
IV, triggering an exception serializationexception because the class does not support serialization
The first chance exception for the "System.Runtime.Serialization.SerializationException" type occurs in mscorlib.dll
System.Runtime.Serialization.SerializationException: Assembly "System.Windows.Forms, version=4.0.0.0, Culture=neutral, The type "System.Windows.Forms.TextBox" in publickeytoken=b77a5c561934e089 is not marked as serializable.
In System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers (RuntimeType type)
In System.Runtime.Serialization.FormatterServices.GetSerializableMembers (type type, StreamingContext context)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo ()
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, Serializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, Serializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize (Object graph, header[] inheaders, __ BinaryWriter Serwriter, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph, header[] headers, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 24
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 128
V, triggering an exception serializationexception because the class does not support serialization
The first chance exception for the "System.Runtime.Serialization.SerializationException" type occurs in mscorlib.dll
System.Runtime.Serialization.SerializationException: Assembly "System.Windows.Forms, version=4.0.0.0, Culture=neutral, The type "System.Windows.Forms.DataGridView" in publickeytoken=b77a5c561934e089 is not marked as serializable.
In System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers (RuntimeType type)
In System.Runtime.Serialization.FormatterServices.GetSerializableMembers (type type, StreamingContext context)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo ()
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, Serializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize (Object obj, isurrogateselector SurrogateSelector, StreamingContext context, Serobjectinfoinit Serobjectinfoinit, Iformatterconverter Converter, Objectwriter Objectwriter, Serializationbinder Binder)
In System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize (Object graph, header[] inheaders, __ BinaryWriter Serwriter, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph, header[] headers, Boolean Fcheck)
In System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize (Stream serializationstream, Object Graph
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 24
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 141
Conclusion: By using serialization and deserialization to binary stream, the object is deeply copied and can only be used if the object supports serializable attributes.
Test deep Copy Method 2
public static object Deepcopyobject (Object obj)
{
Type t = obj. GetType ();
Propertyinfo[] Properties = t.getproperties ();
Object p = t.invokemember ("", System.Reflection.BindingFlags.CreateInstance, NULL, obj, null);
foreach (PropertyInfo pi in properties)
{
if pi. CanWrite)
{
Object value = Pi. GetValue (obj, null);
Pi. SetValue (p, value, NULL);
}
return p;
}
The test results for five scenarios are:
I, does not trigger an exception, but the result is completely wrong
II, no exception is fired, but the result is completely wrong
III, does not trigger an exception, but the result is completely wrong
IV, Text field assignment is correct, but other content is not guaranteed
V, triggering abnormal argumentoutofrangeexception, targetinvocationexception
The first chance exception for the "System.ArgumentOutOfRangeException" type occurs in System.Windows.Forms.dll
The first chance exception for the "System.Reflection.TargetInvocationException" type occurs in mscorlib.dll
System.Reflection.TargetInvocationException: The target of the call has an exception. ---> system.argumentoutofrangeexception: The specified parameter has exceeded the range of valid values.
Parameter name: value
In System.Windows.Forms.DataGridView.set_FirstDisplayedScrollingColumnIndex (Int32 value)
---the end of the inner exception stack trace---
In System.RuntimeMethodHandle.InvokeMethod (Object target, object[] arguments, Signature Sig, Boolean constructor)
In System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal (Object obj, object[] parameters, object[] arguments)
In System.Reflection.RuntimeMethodInfo.Invoke (Object obj, BindingFlags invokeattr, Binder Binder, object[] parameters, CultureInfo culture)
In System.Reflection.RuntimePropertyInfo.SetValue (object obj, Object value, BindingFlags invokeattr, Binder Binder, object[] Index, CultureInfo culture)
In System.Reflection.RuntimePropertyInfo.SetValue (object obj, object value, object[] index)
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 29
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 141
Conclusion: The use of this method for the so-called deep copy, is completely suicidal road!
Test deep Copy Method 3
public static object Deepcopyobject (Object obj)
{
if (obj!= null)
{
object result = Activator.CreateInstance (obj. GetType ());
foreach (FieldInfo field in obj.) GetType (). GetFields ())
{
if field. Fieldtype.getinterface ("IList", false) = = null)
{
field. SetValue (Result, field. GetValue (obj));
else
{
IList listObject = (IList) field. GetValue (result);
if (listObject!= null)
{
foreach (object item in (IList) field. GetValue (obj)))
{
Listobject.add (Deepcopyobject (item);
}
}}} return result;
}
else
{return
null;
}
}
The test results for five scenarios are:
I, can be normal replication (√)
II, can be normal replication (√)
III, did not trigger the exception, the DataTable after replication no row
IV, no exception triggered, text field not assigned
V, no exception fired
Conclusion: This method is only suitable for deep copying of classes with simple structures (such as only the underlying fields, arrays, etc.) in the class, and can be deeply replicated for objects that do not support serialization.
Test deep Copy Method 4
This code source is the same as Method 3
public static object Deepcopyobject (Object obj) {if (obj = = null) return null; Type type = obj.
GetType (); if (type. Isvaluetype | |
Type = = typeof (String) {return obj; else if (type. IsArray) {Type ElementType = Type.GetType (type. Fullname.replace ("[]", String.
Empty));
var array = obj as array; Array copied = Array.CreateInstance (ElementType, array.
Length); for (int i = 0; i < array. Length; i++) {copied. SetValue (Deepcopyobject) (array.
GetValue (i)), i); Return Convert.changetype (copied, obj.
GetType ()); else if (type. IsClass) {Object toret = Activator.CreateInstance (obj.
GetType ()); fieldinfo[] fields = type.
GetFields (BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance); foreach (FieldInfo field in fields) {Object fieldvalue = field.
GetValue (obj);
if (Fieldvalue = = null) continue; Field.
SetValue (Toret, Deepcopyobject (Fieldvalue));
return toret; else throw new ArgumentException ("UnknownType ");
}
The test results for five scenarios are:
I, can be normal replication (√)
II, can be normal replication (√)
III, triggering abnormal missingmethodexception
The first chance exception for the "System.MissingMethodException" type occurs in mscorlib.dll
System.MissingMethodException: There is no parameterless constructor defined for this object.
In System.RuntimeTypeHandle.CreateInstance (RuntimeType type, Boolean publiconly, Boolean NoCheck, boolean& Canbecached, runtimemethodhandleinternal& ctor, boolean& Bneedsecuritycheck)
In System.RuntimeType.CreateInstanceSlow (Boolean publiconly, Boolean Skipcheckthis, Boolean FillCache, StackCrawlMark & Stackmark)
In System.RuntimeType.CreateInstanceDefaultCtor (Boolean publiconly, Boolean Skipcheckthis, Boolean FillCache, stackcrawlmark& Stackmark)
In System.Activator.CreateInstance (type type, Boolean nonpublic)
In System.Activator.CreateInstance (type type)
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 45
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 53
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 99
IV, no exception was fired, but the text field has not been assigned a value successfully
V, triggering exception missingmethodexception
The first chance exception for the "System.MissingMethodException" type occurs in mscorlib.dll
System.MissingMethodException: There is no parameterless constructor defined for this object.
In System.RuntimeTypeHandle.CreateInstance (RuntimeType type, Boolean publiconly, Boolean NoCheck, boolean& Canbecached, runtimemethodhandleinternal& ctor, boolean& Bneedsecuritycheck)
In System.RuntimeType.CreateInstanceSlow (Boolean publiconly, Boolean Skipcheckthis, Boolean FillCache, StackCrawlMark & Stackmark)
In System.RuntimeType.CreateInstanceDefaultCtor (Boolean publiconly, Boolean Skipcheckthis, Boolean FillCache, stackcrawlmark& Stackmark)
In System.Activator.CreateInstance (type type, Boolean nonpublic)
In System.Activator.CreateInstance (type type)
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 45
In DataCopyTest.DataManHelper.DeepCopyObject (Object obj) position d:\MyPrograms\DataCopyTest\DataCopyTest\ DataManHelper.cs: Line No. 53
In the position of DataCopyTest.FormMain.FormMain_Load (Object sender, EventArgs e) d:\MyPrograms\DataCopyTest\DataCopyTest\ FormMain.cs: Line No. 141
Conclusion: This method works like Method 3, and can only deeply replicate classes consisting of basic data types
Specific analysis of specific problems
As can be seen from the example above, it is difficult to find a universal way to copy all the objects deeply. Some deep copy methods that use advanced language features (reflection) cannot be fully grasped by all classes, even if they can be tried successfully on some classes. So I think we should take the following approach to the deep replication of objects:
1, for the class composed of basic data types, for which the serializable tag, directly using serialization and deserialization of the method for deep replication
2, other more complex types such as DataGridView, according to their own situation to write a method for deep copy, said here to write the method according to their own situation, because in the replication of many classes, you only need to copy the attributes that are useful to you. For example, in a TextBox control, only text a property is useful to you, and if you need to use the values of attributes such as ReadOnly in the copied object, then in the copy method that you implement, add the assignment to those attributes. This also has the advantage, is facilitates to carry on some custom development.
The
, as in the following code, is an approximate deep copy of DataGridView, which copies the contents of a DataGridView (DGV) to another DataGridView (dgvtmp). Dgvtmp is then passed to the correlation function to output the contents of the DataGridView to an Excel document:
DataGridView dgvtmp = new DataGridView ();
Dgvtmp.allowusertoaddrows = false; The user is not allowed to produce rows, otherwise the last line for
(int i = 0; i < DGV) is exported. Columns.count; i++)
{
dgvTmp.Columns.Add (DGV). Columns[i]. Name, DGV. Columns[i]. HeaderText);
if (DGV. Columns[i]. DefaultCellStyle.Format.Contains ("N"))//Enables the export of Excel document amount columns to do the sum operation
{
dgvtmp.columns[i]. Defaultcellstyle.format = DGV. Columns[i]. Defaultcellstyle.format;
}
if (!DGV. Columns[i]. Visible)
{
dgvtmp.columns[i]. Visible = false;
}
}
for (int i = 0; i < DGV. Rows.Count; i++)
{
object[] objlist = new OBJECT[DGV. Rows[i]. Cells.count];
for (int j = 0; J < Objlist.length; J + +)
{
if (dgvtmp.columns[j]. DefaultCellStyle.Format.Contains ("N"))
{
Objlist[j] = DGV. Rows[i]. CELLS[J]. Value; Make the export Excel Document Amount column available for sum operations
}
else
{
Objlist[j] = DGV. Rows[i]. CELLS[J]. Editedformattedvalue; The data dictionary is exported by display text
}
dgvTmp.Rows.Add (objlist);
The features of this code are as follows:
1, DataGridView Property allowusertoaddrows to set to false, otherwise exported to an Excel document, you will find that the end of a more empty line.
2, we are here to mark those columns are hidden columns, so in the subsequent processing, if you want to delete these columns, then delete the Dgvtmp column rather than the DGV column, protect the original data.
3, for some of the data dictionary translation, we preach not value but editedformattedvalue, this way directly using the DGV on the screen to display the translated text, rather than the original data dictionary values.
4, for part of the amount of the column, you need to pass the value directly, and you need to set the Defaultcellstyle.format of the column, so that these content after the output to Excel document, you can do the sum operation (Excel, similar to "12,345.67" A string is not able to do a sum operation.