很多时候我们复制一个对象实例A到实例B,在用实例B去做其他事情的时候,会对实例B进行修改,为保证对B的修改不会影响到A的正常使用,就需要使用到深复制。
我在网上搜到一些深复制的方法,同时写了几组例子对这些方法进行测试。
我的操作系统版本为Win7旗舰版,.NET Framework版本是4.5
测试程序
我建了一个C#窗体应用程序(Winform),其主窗口FormMain的Load函数内容如下:
private void FormMain_Load(object sender, EventArgs e) { //测试1:深度复制 自定义类 try { Console.WriteLine("=== 深度复制 自定义类 ==="); TestClass test1 = new TestClass(); test1.a = 10; test1.b = "hello world!"; test1.c = new string[] { "x", "y", "z" }; TestClass test2 = new TestClass(); test2.a = 11; 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()); }</div>
//测试2:深度复制 可序列化的自定义类
try { Console.WriteLine("=== 深度复制 可序列化的自定义类 ==="); TestClassWithS test1 = new TestClassWithS(); test1.a = 10; test1.b = "hello world!"; test1.c = new string[] { "x", "y", "z" }; TestClassWithS test2 = new TestClassWithS(); test2.a = 11; 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()); }</div>
//测试3:深度复制 DataTable
try { Console.WriteLine("=== 深度复制 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("=== 复制后 ==="); 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(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }</div>
//测试4:深度复制 TextBox
try { Console.WriteLine("=== 深度复制 TextBox ==="); txtTest.Text = "1234"; Console.WriteLine("复制前:" + txtTest.Text); TextBox txtTmp = new TextBox(); txtTmp = (TextBox)DataManHelper.DeepCopyObject(txtTest); Console.WriteLine("复制后:" + txtTmp.Text); } catch (Exception ex) { Console.WriteLine(ex.ToString()); }</div>
//测试5:深度复制 DataGridView
try { Console.WriteLine("=== 深度复制 DataGridView ==="); DataGridView dgvTmp = new DataGridView(); dgvTmp = (DataGridView)DataManHelper.DeepCopyObject(dgvTest); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } }</div>
其中txtTest是一个测试用的TextBox,dgvTmp是一个测试用的DataGridView,TestClass是一个自定义类,TestClassWithS是添加了Serializable特性的TestClass类,它们的具体实现如下:
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; } } //支持序列化的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; } } }</div>
我对每个搜来的深复制方法,都用了这五个类的实例进行深复制测试,这五个类的特征如下:
I、对自定义类TestClass进行深复制测试
II、对自定义类TestClassWithS进行深复制测试,TestClassWithS是添加了Serializable特性的TestClass类
III、对DataTable进行深复制测试
IV、对控件TextBox进行深复制测试
V、对控件DataGridView进行深复制测试
我们通过实现方法DataManHelper.DeepCopyObject来进行测试
测试深复制方法1
使用二进制流的序列化与反序列化深度复制对象
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; }</div>
五个场景的测试结果为:
I、触发异常SerializationException,原因是该类不支持序列化
“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“DataCopyTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中的类型“DataCopyTest.TestClass”未标记为可序列化。
在 System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
在 System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit s