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

 更新时间:2020年6月25日 11:29  点击:1623

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

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

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25