.NET的深复制方法(以C#语言为例)

很多时候我们复制一个对象实例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());
 }

//测试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());
 }

//测试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());
 }

//测试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());
 }

//测试5:深度复制 DataGridView

 try
 {
  Console.WriteLine("=== 深度复制 DataGridView ===");
  DataGridView dgvTmp = new DataGridView();
  dgvTmp = (DataGridView)DataManHelper.DeepCopyObject(dgvTest);
 }
 catch (Exception ex)
 {
  Console.WriteLine(ex.ToString());
 }
}

其中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;
  }
 }
}

我对每个搜来的深复制方法,都用了这五个类的实例进行深复制测试,这五个类的特征如下:

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;
}

五个场景的测试结果为:

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 serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, S“DataCopyTest.vshost.exe”(托管(v4.0.30319)): 已加载“C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll”
erializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 37
II、可正常复制 (√)

III、可正常复制 (√)

IV、触发异常SerializationException,原因是该类不支持序列化

“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中的类型“System.Windows.Forms.TextBox”未标记为可序列化。
   在 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 serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 128
V、触发异常SerializationException,原因是该类不支持序列化

“System.Runtime.Serialization.SerializationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Runtime.Serialization.SerializationException: 程序集“System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089”中的类型“System.Windows.Forms.DataGridView”未标记为可序列化。
   在 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 serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   在 System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 24
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:利用序列化与反序列化到二进制流的方法深复制对象,只有在该对象支持Serializable特性时才可使用

测试深复制方法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;
}

五个场景的测试结果为:

I、不会触发异常,但结果完全错误

II、不会触发异常,但结果完全错误

III、不会触发异常,但结果完全错误

IV、Text字段赋值结果正确,但其他内容不能保证

V、触发异常ArgumentOutOfRangeException、TargetInvocationException

“System.ArgumentOutOfRangeException”类型的第一次机会异常在 System.Windows.Forms.dll 中发生
“System.Reflection.TargetInvocationException”类型的第一次机会异常在 mscorlib.dll 中发生
System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.ArgumentOutOfRangeException: 指定的参数已超出有效值的范围。
参数名: value
   在 System.Windows.Forms.DataGridView.set_FirstDisplayedScrollingColumnIndex(Int32 value)
   --- 内部异常堆栈跟踪的结尾 ---
   在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   在 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   在 System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 29
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:使用这种方法进行所谓深复制,完全是自寻死路!

测试深复制方法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;
 }
}

五个场景的测试结果为:

I、可正常复制(√)

II、可正常复制(√)

III、未触发异常, 复制后DataTable无行列

IV、未触发异常,Text字段未赋值

V、未触发异常

结论:这个方法只适用于深复制具备简单结构的类(如类中只有基础字段、数组等),对于不支持序列化的对象也可以进行深复制。

测试深复制方法4

这段代码来源同方法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("Unknown type");
}

五个场景的测试结果为:

I、可正常复制(√)

II、可正常复制(√)

III、触发异常MissingMethodException

“System.MissingMethodException”类型的第一次机会异常在 mscorlib.dll 中发生
System.MissingMethodException: 没有为该对象定义无参数的构造函数。
   在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
   在 System.Activator.CreateInstance(Type type)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 45
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 53
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 99
IV、未触发异常,但Text字段也未赋值成功

V、触发异常MissingMethodException

“System.MissingMethodException”类型的第一次机会异常在 mscorlib.dll 中发生
System.MissingMethodException: 没有为该对象定义无参数的构造函数。
   在 System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   在 System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
   在 System.Activator.CreateInstance(Type type, Boolean nonPublic)
   在 System.Activator.CreateInstance(Type type)
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 45
   在 DataCopyTest.DataManHelper.DeepCopyObject(Object obj) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\DataManHelper.cs:行号 53
   在 DataCopyTest.FormMain.FormMain_Load(Object sender, EventArgs e) 位置 d:\MyPrograms\DataCopyTest\DataCopyTest\FormMain.cs:行号 141
结论:这个方法的作用类似方法3,只能深复制基本数据类型组成的类

具体问题具体分析

从上面的例子可以看出,想找一个放之四海而皆准的方式去深复制所有对象是很困难的。一些使用高级语言特性(反射)的深复制方法,即使可以在部分类上试用成功,也无法对所有的类都具备十足的把握。因此我认为应该采取下面的方式处理对象的深复制问题:

1、对于由基本数据类型组成的类,为之打上Serializable标签,直接使用序列化与反序列化的方法进行深复制

2、其他较为复杂的类型如DataGridView,可根据自身情况写一个方法进行深复制,之所以在这里说要根据自身情况写方法,是因为在对很多类进行复制时,你只需要复制对你有用的属性就行了。如TextBox控件中,只有Text一个属性对你是有用的,如果你需要在复制后的对象中用到Readonly等属性的值,那么在你自己实现的复制方法中,也加上对这些属性的赋值即可。这样做还有一个好处,就是方便进行一些定制化的开发。

如下面这段代码,就是对DataGridView的一个近似的深复制,这段代码将一个DataGridView(dgv)的内容复制到另一个DataGridView(dgvTmp)中,然后将dgvTmp传递给相关函数用于将DataGridView中的内容输出到Excel文档:

DataGridView dgvTmp = new DataGridView();
dgvTmp.AllowUserToAddRows = false; //不允许用户生成行,否则导出后会多出最后一行
for (int i = 0; i < dgv.Columns.Count; i++)
{
 dgvTmp.Columns.Add(dgv.Columns[i].Name, dgv.Columns[i].HeaderText);
 if (dgv.Columns[i].DefaultCellStyle.Format.Contains("N")) //使导出Excel文档金额列可做SUM运算
 {
 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; //使导出Excel文档金额列可做SUM运算
 }
 else
 {
 objList[j] = dgv.Rows[i].Cells[j].EditedFormattedValue; //数据字典按显示文字导出
 }
 }
 dgvTmp.Rows.Add(objList);
}

这段代码的特点如下:

1、DataGridView的属性AllowUserToAddRows要设置成false,否则导出到Excel文档后,会发现最后会多出一个空行。

2、我们在这里标记了那些列是隐藏列,这样在后面的处理中,如果要删除这些列,那删除的也是dgvTmp的列而不是dgv的列,保护了原数据。

3、对于部分数据字典的翻译,我们传的不是Value而是EditedFormattedValue,这种方式直接使用了dgv在屏幕上显示的翻译后文字,而不是原来的数据字典值。

4、对于部分金额列,需要直接传Value值,同时需要设置该列的DefaultCellStyle.Format,这样可使得这些内容在之后输出到Excel文档后,可做求和运算(Excel中类似“12,345.67”字符串是不能做求和运算的)。

(0)

相关推荐

  • c#对象反序列化与对象序列化示例详解

    1.对象序列化的介绍 (1).NET支持对象序列化的几种方式二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下.SOAP序列化:对象序列化之后的结果符合SOAP协议,也就是可以通过SOAP 协议传输,通过System.Runtime.Serialization.Formatters.Soap命名空间下的SoapFormatter类来实现的.XML

  • C#复制和深度复制的实现方法

    深度复制与浅表复制的区别在于,浅表复制只复制值类型的值,而对于实例所包含的对象依然指向原有实例. class Program { [Serializable] public class Car { public string name; public Car(string name) { this.name = name; } } [Serializable] public class Person:ICloneable { public int id; public string name;

  • C#实现的序列化通用类实例

    本文实例讲述了C#实现的序列化通用类.分享给大家供大家参考.具体如下: using System; using System.IO; using System.IO.Compression; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Soap; using System.Text; using System.Xml; using Syste

  • 用序列化实现List<T> 实例的深复制(推荐)

    如果 List<T> 里的 T 是引用类型(如myClass类),则在 像这种写法: List<myClass> lists1 = new List<myClass>() { new myClass(), new myClass() }; List<myClass> lists2 = new List<myClass>(lists1 ); 其实是一个浅复制的过程. 如果要实现深复制,通过有几种做法,可以用foreach, 或者重写Clone()方

  • C#实现json的序列化和反序列化实例代码

    在做asp.net和unity进行http通信的时候,当unity客户端发出表单请求的时候,我要将他要请求的数据以json的格式返回给客户端,让客户端来解析.服务器端这一块就涉及到json的序列化和反序列化的问题. 两种方法都有例子,第一种是用泛型集合来存储的对象,然后将集合序列化一下:第二种是直接序列化的一个对象 复制代码 代码如下: using System;using System.Collections.Generic;using System.Web.Script.Serializat

  • .NET的深复制方法(以C#语言为例)

    很多时候我们复制一个对象实例A到实例B,在用实例B去做其他事情的时候,会对实例B进行修改,为保证对B的修改不会影响到A的正常使用,就需要使用到深复制. 我在网上搜到一些深复制的方法,同时写了几组例子对这些方法进行测试. 我的操作系统版本为Win7旗舰版,.NET Framework版本是4.5 测试程序 我建了一个C#窗体应用程序(Winform),其主窗口FormMain的Load函数内容如下: private void FormMain_Load(object sender, EventAr

  • 如何短时间内学好一门语言 shell脚本语言为例

    这里我分享的是学习计算机shell脚本语言的方法,可能你不是在软件行业,但我建议也看看,因为我并没有讲什么技术细节,主要分享的是提炼出的通用性方法,这是对学习任何东西都是有用的. 当然,如果你好奇心足够强的话,建议也可以按照下面的方法来尝试一下,因为科学家已经预测:以后的世界就是一个软件定义的世界,机器人和人工智能会非常普及,而支持它们运行的除了我们看得见的硬件,最关键的就是里面的软件. 所以,学会编程以后就不仅仅只是软件从业人员需要掌握的技能,任何人都可以掌握它,既可以用它来不断提高工作效率,

  • 改进版通过Json对象实现深复制的方法

    之前看到博客的关于Json对象的深复制方法,即 复制代码 代码如下: var obj = { sayName: function() { alert(this.name); }, name:'静水渊' };var cloneObj=JSON.parse(JSON.stringify(obj));cloneObj.sayName(); 但这种方式,不能复制属性值是函数的属性,因此改进了方法,以下是具体代码: 复制代码 代码如下: var obj = { sayName: function() {

  • 浅谈Javascript中深复制

    在javascript中,所有的object变量之间的赋值都是传地址的,可能有同学会问哪些是object对象.举例子来说明可能会比较好: 复制代码 代码如下: typeof(true)    //"boolean" typeof(1)       //"number" typeof("1")     //"string" typeof({})      //"object" typeof([])     

  • C++、python和go语言实现的简单客户端服务器代码示例

    工作中用到了C/S模型,所做的也无非是给服务器发数据,但开发阶段会遇到程序自身的回环测试,需要用到简单的服务端以便验证数据发送的正确性. 写软件用C++,跑测试用python,这段时间也刚好看go语言,所以都要有demo.以下三组程序实现的功能相同,这里一起做下总结. 一.C++实现 Boost.Asio是一个跨平台的C++库,它用现代C++方法为网络和底层I/O程序提供了一致的异步I/O模型. 为了跨平台,我用boost库实现,具体如下. 服务端代码: 复制代码 代码如下: /*      F

  • Nodejs为什么选择javascript为载体语言

    我们先来看看NodeJS官网上的介绍: 其特点为: 1,它是一个Javascript运行环境 2,依赖于Chrome V8引擎进行代码解释 3,事件驱动 4, 非阻塞I/O 5, 轻量.可伸缩,适于实时数据交互应用 6,单进程,单线程 (1),Nodejs为什么选择javascript为载体语言 事实上,在实现 Node.js 之初,作者 Ryan Dahl 并没有选择 JavaScript,他尝试过 C.Lua,皆因其欠缺一些高级语言的特性,如闭包.函数式编程,致使程序复杂,难以维护. 而 J

  • C语言实现加密解密功能

    加密主要是通过一种算法对原内容进行处理,使原来内容不直观可见.解密过程通常要知道加密的算法,然后对加密后的内容进行逆处理,从而实现解密功能.当然解密也有一些暴力破解的方法.接下来以 c 语言 为例讲解一种简单的加密解密以及暴力破解字符串的方法,带您走进加密解密技术的大门. 先讲一下凯撒加密,凯撒密码相传是古罗马凯撒大帝用来保护重要军情的加密系统.它是一种置换密码,通过将字母顺序推后起到加密作用.如字母顺序推后 3 位,字母 A 将被推作字母 D,字母 B 将被推作字母 E.本实例类似于凯撒加密.

  • C语言规律循环累加求和案例

    我就废话不多说了,大家还是直接看代码吧~ #include <stdio.h> void main(){ int i; float a,b,c,d,t; a = 1.0; b = 2.0; i = 1; t = 0; for (i = 1; i <= 20; i++) { if (i < 2){ c = b / a; t = c+t; a = a + 1; b = b + 1; //第一项不符合规律 } else { c = b / a; t = t + c; d = a; a =

  • c语言实现从源文件从文本到可执行文件经历的过程

    目录 源文件从文本到可执行文件经历的过程 从 .c文件到 .exe文件 源程序如何到可执行程序 源文件从文本到可执行文件经历的过程 从 .c文件到 .exe文件 gcc hello.c -o hello.exe 源文件( -E)  预处理文件 (-S) 汇编文件 (-c) 目标文件 ( 无)可执行文件 hello.c--预处理--hello.i--编译--hello.s---汇编--hello.o--链接--hello.exe 预处理 gcc -E hello.c -o hello.i 1.头文

  • 浅谈Python浅拷贝、深拷贝及引用机制

    这礼拜碰到一些问题,然后意识到基础知识一段时间没巩固的话,还是有遗忘的部分,还是需要温习,这里做份笔记,记录一下 前续 先简单描述下碰到的题目,要求是写出2个print的结果 可以看到,a指向了一个列表list对象,在Python中,这样的赋值语句,其实内部含义是指a指向这个list所在内存地址,可以看作类似指针的概念. 而b,注意,他是把a对象包裹进一个list,并且乘以5,所以b的样子应该是一个大list,里面元素都是a 而当a对象进行了append操作后,其实,隐含的意思是,内存中的这个l

随机推荐