简单谈谈C#中深拷贝、浅拷贝

Object.MemberwiseClone 方法

创建当前 Object 的浅表副本。

protected Object MemberwiseClone()

MemberwiseClone 方法创建一个浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。 如果字段是值类型的,则对该字段执行逐位复制。 如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

例如,考虑对象X引用对象 A 和 B , 对象 B 依次引用对象 C。 X 的浅表副本创建一个新对象 X2,该对象也引用对象 A 和 B。 相比而言,X 的深层副本创建一个新对象 X2,该对象引用新对象 A2 和 B2(分别为 A 和 B 的副本)。 B2 又引用新对象 C2,C2 是 C 的副本。 该示例阐释了浅层和深层复制操作之间的区别。

有很多方法可以实现深层复制操作,前提是浅表复制操作由 MemberwiseClone 方法执行但不符合您的需求。

这些要求包括:

调用要复制的对象的类构造函数以创建含有从第一个对象中提出的属性值的第二个对象。 这假定对象的值完全由类构造函数定义。

调用 MemberwiseClone 方法创建的对象的浅表副本,然后将指定新的对象,其值均相同,原始对象的任何属性或字段的值是引用类型。 该示例中的 DeepCopy 方法阐释了这种方法。

序列化要深层复制的对象,然后将序列化的数据还原到另一个对象变量。

使用带递归的反射执行的深层复制操作。

下面的示例演示 MemberwiseClone 方法。 它定义了 ShallowCopy 方法,该方法通过调用 MemberwiseClone 方法来在 Person 对象上执行浅表复制操作。 它还定义了在 Person 对象上执行深层复制操作的DeepCopy 方法。

using System;

public class IdInfo
{
  public int IdNumber;

  public IdInfo(int IdNumber)
  {
    this.IdNumber = IdNumber;
  }
}

public class Person
{
  public int Age;
  public string Name;
  public IdInfo IdInfo;

  public Person ShallowCopy()
  {
    return (Person)this.MemberwiseClone();
  }

  public Person DeepCopy()
  {
    Person other = (Person) this.MemberwiseClone();
    other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
    return other;
  }
}

public class Example
{
  public static void Main()
  {
    // Create an instance of Person and assign values to its fields.
    Person p1 = new Person();
    p1.Age = 42;
    p1.Name = "Sam";
    p1.IdInfo = new IdInfo(6565);

    // Perform a shallow copy of p1 and assign it to p2.
    Person p2 = (Person) p1.ShallowCopy();

    // Display values of p1, p2
    Console.WriteLine("Original values of p1 and p2:");
    Console.WriteLine("  p1 instance values: ");
    DisplayValues(p1);
    Console.WriteLine("  p2 instance values:");
    DisplayValues(p2);

    // Change the value of p1 properties and display the values of p1 and p2.
    p1.Age = 32;
    p1.Name = "Frank";
    p1.IdInfo.IdNumber = 7878;
    Console.WriteLine("\nValues of p1 and p2 after changes to p1:");
    Console.WriteLine("  p1 instance values: ");
    DisplayValues(p1);
    Console.WriteLine("  p2 instance values:");
    DisplayValues(p2);

    // Make a deep copy of p1 and assign it to p3.
    Person p3 = p1.DeepCopy();
    // Change the members of the p1 class to new values to show the deep copy.
    p1.Name = "George";
    p1.Age = 39;
    p1.IdInfo.IdNumber = 8641;
    Console.WriteLine("\nValues of p1 and p3 after changes to p1:");
    Console.WriteLine("  p1 instance values: ");
    DisplayValues(p1);
    Console.WriteLine("  p3 instance values:");
    DisplayValues(p3);
  }

  public static void DisplayValues(Person p)
  {
    Console.WriteLine("   Name: {0:s}, Age: {1:d}", p.Name, p.Age);
    Console.WriteLine("   Value: {0:d}", p.IdInfo.IdNumber);
  }
}
// The example displays the following output:
//    Original values of p1 and p2:
//     p1 instance values:
//       Name: Sam, Age: 42
//       Value: 6565
//     p2 instance values:
//       Name: Sam, Age: 42
//       Value: 6565
//
//    Values of p1 and p2 after changes to p1:
//     p1 instance values:
//       Name: Frank, Age: 32
//       Value: 7878
//     p2 instance values:
//       Name: Sam, Age: 42
//       Value: 7878
//
//    Values of p1 and p3 after changes to p1:
//     p1 instance values:
//       Name: George, Age: 39
//       Value: 8641
//     p3 instance values:
//       Name: Frank, Age: 32
//       Value: 7878

为了实现深度复制,我们就必须遍历有相互引用的对象构成的图,并需要处理其中的循环引用结构。这无疑是十分复杂的。幸好借助.Net的序列化和反序列化机制,可以十分简单的深度Clone一个对象。

原理很简单,首先将对象序列化到内存流中,此时对象和对象引用的所用对象的状态都被保存到内存中。.Net的序列化机制会自动处理循环引用的情况。然后将内存流中的状态信息反序列化到一个新的对象中。

这样一个对象的深度复制就完成了。在原型设计模式中CLONE技术非常关键。

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace CloneDemo
{
  [Serializable]
  class DemoClass
  {
    public int i = 0;
    public int[] iArr = { 1, 2, 3 };

    public DemoClass Clone1() //浅CLONE
    {
      return this.MemberwiseClone() as DemoClass;
    }

    public DemoClass Clone2() //深clone
    {
      MemoryStream stream = new MemoryStream();
      BinaryFormatter formatter = new BinaryFormatter();
      formatter.Serialize(stream, this);
      stream.Position = 0;
      return formatter.Deserialize(stream) as DemoClass;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      DemoClass a = new DemoClass();
      a.i = 10;
      a.iArr = new int[] { 8, 9, 10 };
      DemoClass b = a.Clone1();
      DemoClass c = a.Clone2();

      // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化 而 c不会变化
       a.iArr[0] = 88;

      Console.WriteLine("MemberwiseClone");
      Console.WriteLine(b.i);
      foreach (var item in b.iArr)
      {
        Console.WriteLine(item);
      }

      Console.WriteLine("Clone2");
      Console.WriteLine(c.i);
      foreach (var item in c.iArr)
      {
        Console.WriteLine(item);
      }

      Console.ReadLine();
    }
  }
}

以上所述就是本文的全部内容了,希望大家能够喜欢。

(0)

相关推荐

  • C#中4种深拷贝方法介绍

    1:利用反射实现 public static T DeepCopy<T>(T obj) { //如果是字符串或值类型则直接返回 if (obj is string || obj.GetType().IsValueType) return obj; object retval = Activator.CreateInstance(obj.GetType()); FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | B

  • C# 拷贝数组的几种方法(总结)

    突然学到了,所以就放到博客上来共享一下,权当是学习日记吧. 首先说明一下,数组是引用类型的,所以注意不要在复制时复制了地址而没有复制数值哦! 其实在复制数组的时候,一定要用new在堆中开辟一块新的空间专门用于存放数组,这样才是有效的. (1) int[] pins = { 9, 3, 7, 2 }; int[] copy=new int[pins.length]; for (int i = 0; i < copy.length; i++) { copy[i] = pins[i]; } (2) i

  • C#运用FileInfo类实现拷贝文件的方法

    本文所述实例为C#运用FileInfo类实现拷贝文件的方法,程序中C#首先创建一个 StreamWriter 对象 writer,它向 FileInfo 的实例 srcFile 所表示的文件追加文本,FileInfo 类的 CopyTo 方法,实现文件的拷贝. 具体实现代码如下: using System; using System.IO; namespace 拷贝文件 { class Class1 { [STAThread] static void Main(string[] args) {

  • c# 深拷贝与浅拷贝的区别分析及实例

    深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响.举个例子,一个人名叫张三,后来用他克隆(假设法律允许)了另外一个人,叫李四,不管是张三缺胳膊少腿还是李四缺胳膊少腿都不会影响另外一个人.比较典型的就是Value(值)对象,如预定义类型Int32,Double,以及结构(struct),枚举(Enum)等. 浅拷贝是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同).对其中任何一个对象的改动都会影响另外一个对象.举个例子,一个人一开始叫张三,后来

  • C#实现屏幕拷贝的方法

    本文实例讲述了C#实现屏幕拷贝的方法.分享给大家供大家参考.具体如下: 方法一: using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Drawing.Drawing2D; namespace Wi

  • c#深拷贝文件夹示例

    复制代码 代码如下: using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Text.RegularExpressions;using System.Threading.Tasks; namespace FileUtility{    public class Program    {        public static v

  • C#拷贝文件简单实现方法

    本文实例讲述了C#拷贝文件简单实现方法.分享给大家供大家参考.具体分析如下: C#中拷贝文件可以先通过FileInfo指定文件,然后通过FileInfo的CopyTo方法拷贝文件到指定的位置 FileInfo fi = new FileInfo(@"C:\file.txt"); if(fi.Exists) { fi.CopyTo(@"C:\copyfile.txt"); } 希望本文所述对大家的C#程序设计有所帮助.

  • C#通过指针实现快速拷贝的方法

    本文实例讲述了C#通过指针实现快速拷贝的方法.分享给大家供大家参考.具体实现方法如下: // fastcopy.cs // 编译时使用:/unsafe using System; class Test { // unsafe 关键字允许在下列 // 方法中使用指针: static unsafe void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int count) { if (src == null || srcIndex

  • C#浅拷贝和深拷贝实例解析

    在有些时候,我们需要从数据库读取数据填充对象或从硬盘读取文件填充对象,但是这样做相对耗时.这时候我们就想到了对象的拷贝.本文即以实例形式解析了C#浅拷贝和深拷贝的用法.具体如下: 一.浅拷贝 1.什么是"浅拷贝": 当针对一个对象前拷贝的时候,对于对象的值类型成员,会复制其本身,对于对象的引用类型成员,仅仅复制对象引用,这个引用指向托管堆上的对象实例. 2.有一个对象,包含引用类型的类成员和值类型的struct成员 Cinema包含引用类型成员Room和值类型成员Film. publi

  • C#实现windows form拷贝内容到剪贴板的方法

    本文实例讲述了C#实现windows form拷贝内容到剪贴板的方法.分享给大家供大家参考.具体实现方法如下: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Data; using System.Windows.Forms; public class MainClass { public static void Main(string[

随机推荐