简单谈谈C#中深拷贝、浅拷贝
作者:hebedich 发布时间:2022-06-25 04:36:56
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();
}
}
}
以上所述就是本文的全部内容了,希望大家能够喜欢。
猜你喜欢
- 概述:利用AccessibilityService机制实现了一个比较好玩儿的功能,微信朋友圈自动遍历点赞。即通过不断的滚动+点赞实现把每一条
- 基本原理:利用URLConnection获取要下载文件的长度、头部等相关信息,并设置响应的头部信息。并且通过URLConnection获取输
- 一、代码实例实现功能将Array转换为List将List转换为Array将Array转换为Dictionary将Dictionary转换为A
- 1.引言合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务
- 一.前言在以前的项目中,很少去关注spring aop的具体实现与理论,只是简单了解了一下什么是aop具体怎么用,看到了一篇博文写得还不错,
- Springboot 实体类生成数据库表JPA:springboot -jpa:数据库的一系列的定义数据持久化的标准的体系学习的目的是:利用
- Maven工程分模块开发完成 父工程配置了 tomcat7插件,运行 run命令执行run指令时失败,报错信息如下:D:\java\JDK8
- //构造文件File类File f=new File(fileName);//判断是否为目录f.isDirectory();//获取目录下的
- 1 依赖配置<parent> <groupId>org.springframework.b
- 继承的概念继承是面向对象编程中的一个概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类在继承父类的同时也
- MyBatis * 介绍MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是 * 功能。那么 * 拦截MyBatis
- 最近有一款2048的游戏非常火,本文将来介绍一下使用OGEngine游戏引擎开发游戏2048。OGEngine引擎是开源的,我们很容易找到,
- 本文实例为大家分享了Java使用poi操作excel的具体代码,供大家参考,具体内容如下依赖poi的jar包,pom.xml配置如下:<
- 前几天,公司数据库出现了两条相同的数据,而且时间相同(毫秒也相同)。排查原因,发现是网络波动造成了重复提交。由于网络波动而重复提交的例子也比
- 在Android Studio中对一个自己库进行生成操作时将会同时生成*.jar与*.aar文件。分别存储位置: &n
- 一、创建项目创建一个简单的Java项目,其中Main.java为主函数,包含main方法:二、完成JAR配置进入File->Proje
- 一、AtomicReference 基本使用我们这里再聊起老生常谈的账户问题,通过个人银行账户问题,来逐渐引入 AtomicReferenc
- 前言springmvc中有两种很普遍的AOP实现:1.过滤器(Filter)2. * (Interceptor)本篇面对的是一些刚接触spr
- 介绍备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获并保存一个对象的内部状态,并在之后可
- 记录自己用java swing做的第一个简易界面。LoginAction.javapackage com.QQUI0819;import j