C# 中的浅拷贝和深拷贝

article/2025/10/5 14:28:10

在本文中,将通过示例讨论C#中浅拷贝和深拷贝。这是上一篇文章的续篇。因此,在继续本文之前,请阅读以前的文章,其中之前使用示例讨论了C#中原型设计模式

什么是深拷贝和浅拷贝?

浅复制和深复制在复制原型设计模式中的对象中起着重要作用。因此,在本文中,将通过示例讨论什么是“浅复制”和“深复制”以及它们之间的区别。

所谓的对象拷贝(复制)就是为对象创建副本,得到相同的对象。先直接给出深拷贝和浅拷贝的区别如下:

1)深拷贝:完全将对象中的所有字段(引用类型和值类型等)都复制到副本对象中,这些字段都会被重新创建并且复制,副本对象内的值并不会因为源对象数据的值的修改而跟着发生改变。(也就是说深拷贝出来的副本对象中,对象里的数据如果是值类型,栈内容是其值本身;对于引用类型,其值是托管堆中保存的具体的值,而不是托管堆的内存地址。因此对拷贝出来的副本对象的修改不会反映到被拷贝的源对象上。深拷贝本质上就是软件设计模式里的原型模式。与C#对应的接口是ICloneable。)

2)浅拷贝:简单的复制栈的内容,对于值类型,栈内容是其值本身,对于引用类型,其值为托管堆的内存地址,对拷贝的对象的修改会反映到被拷贝的对象。(同样都是将对象中的所有字段都复制到副本对象中,副本对象里的数据如果是值类型,栈内容是其值本身,在源数据内修改值类型,副本的值类型不会发生改变,因为值类型本身在栈内有不同的地址。但是如果副本对象的数据是引用类型,由于浅拷贝只是拷贝引用类型值的引用,也就是堆的地址,所以副本对象的引用类型数据发生改变时,源对象中的引用类型数据也会跟着改变

需要注意的是,无论是哪种拷贝,微软都建议使用类型继承ICloneable接口的方式明确告诉调用者,该对象是否可用被拷贝。当然了,ICloneable接口只提供了一个声明为Clone的方法,我们可以根据需求在Clone的方法内实现浅拷贝或者是深拷贝;

另外,由于String类型理论上是引用类型,但是由于该引用类型的特殊性,Object.MemberwiseClone方法仍旧为他创建了副本,也就是说,在浅拷贝过程中,我们应该将字符串看成值类型。

【示例】浅拷贝:

如果是“浅拷贝”,它将从现有对象创建新对象,然后将当前对象的值类型字段拷贝到新对象。但是在引用类型的情况下,它将仅拷贝引用类型的引用(也就是引用类型数据的地址),而不拷贝引用类型本身的值。因此,在引用类型的情况下,原始引用和克隆引用是相同的对象。为了更好地理解这一点,请看下图。

understanding Shallow Copy in C# with example

如上图所示,首先我们创建一个对象emp1,然后使用一些值初始化该对象。然后,我们使用GetClone方法创建第二个对象emp2。如存储器中表示所示,值类型字段(Name和Department)被复制并存储在不同的存储器位置,而引用类型字段(即EmpAddress)仍指向相同的旧存储器位置。这意味着现在,对象emp1和emp2现在都引用相同的Address对象的地址。因此,如果我们对emp1和emp2地址进行任何更改,那么它们将相互影响。

【示例】深拷贝:

对于深度拷贝,它将从现有对象创建新对象,然后将当前对象的字段复制到新创建的对象。如果该字段是值类型,则将执行该字段的逐位复制。如果该字段是引用类型,则将创建引用对象的新副本。

Deep Copy in C#

如上图所示,“名称”和“部门”属性是值类型,因此它将创建该副本的副本并将其存储在其他位置。EmpAddress是引用类型属性,并且在深拷贝中,存在引用类型字段的克隆,该字段也将存储在其他位置。因此,你需要牢记的一点是,对于深拷贝,字段类型是值类型还是引用类型都无关紧要。它始终会复制整个数据,并将其存储在其他存储位置。 

微软提供浅拷贝的MemberwiseClone方法

受保护的方法,只能本类或子类调用。

protected object MemberwiseClone ();

1)目的:用于创建当前Object的浅表副本。

2)示例:下面代码说明了MemberwiseClone方法的使用。分别定义了一个浅拷贝方法ShallowCopy()和一个深拷贝的方法DeepCopy()。

using System;//IdInfo类
public class IdInfo
{public int IdNumber;public IdInfo(int IdNumber){this.IdNumber = IdNumber;}
}//Person类
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(IdInfo.IdNumber);other.Name = String.Copy(Name);return other;}
}public class Example
{//代码执行public static void Main(){// 创建Person实例并为其字段赋值Person p1 = new Person();p1.Age = 42;p1.Name = "Sam";p1.IdInfo = new IdInfo(6565);// 执行p1的浅拷贝并将其分配给p2Person p2 = p1.ShallowCopy();// 输出p1和p2的值Console.WriteLine("Original values of p1 and p2:");Console.WriteLine("   p1 instance values: ");DisplayValues(p1);Console.WriteLine("   p2 instance values:");DisplayValues(p2);// 更改p1属性的值并输出p1和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);// 制作一个p1的深拷贝并将其分配给p3Person p3 = p1.DeepCopy();// 将p1的成员更改为新值以显示深度副本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);}
}
//       以上代码示例输出结果如下所示:
//       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

3)备注:如果MemberwiseClone方法执行的浅拷贝操作不能满足你的需求,则有很多方法可以实现深层赋值操作。其中包括:

A、调用要拷贝的对象的类构造函数,以使用从第一个对象获取的属性值来创建第二个对象。假定对象的值完全由其类构造函数定义。

B、调用MemberwiseClone方法以创建对象的浅拷贝副本,然后对浅拷贝副本为引用类型的任何属性或字段赋值,上面的DeepCopy()方法说明了这种方式。

C、序列化需要深拷贝的原始对象,然后将序列化的数据还原到其他对象变量。

D、结合使用反射和递归来执行深拷贝操作。


http://chatgpt.dhexx.cn/article/1G5e2BCw.shtml

相关文章

JAVA 浅拷贝和深拷贝

拷贝 拷贝即对已有的数据创建一个副本,在 Java 中,拷贝可分为引用拷贝、浅拷贝、深拷贝。 引用拷贝 在 Java 中,实例化后的对象存储在堆区,而局部变量存放在局部变量表(栈)中,如: public void yinYongC…

js浅拷贝和深拷贝

1、JS数据类型 基本数据类型:Boolean、String、Number、null、undefined 引用数据类型:Object、Array、Function、RegExp、Date等 2、深拷贝与浅拷贝 深拷贝和浅拷贝都只针对引用数据类型, 浅拷贝会对对象逐个成员依次拷贝,但…

C++浅拷贝和深拷贝

1、浅拷贝 浅拷贝:又称值拷贝,将源对象的值拷贝到目标对象中去,本质上来说源对象和目标对象共用一份实体,只是所引用的变量名不同,地址其实还是相同的。 举个简单的例子,你的小名叫西西,大名叫…

彻底理解Python中浅拷贝和深拷贝的区别

目录 前言 1. 浅拷贝和深拷贝的概念 2. is和的区别 3. 赋值操作 4. copy模块里面的copy()方法 5. copy模块里面的deepcopy()方法 6.字典自带的copy方法 7.切片表达式拷贝 8.总结 前言 Python 的所有变量其实都是指向内存中的对象的一个指针,这确实和之前学…

如何理解java的回调函数?

对于技术问题,会用是一回事,理解这个技术问题的来龙去脉、设计者当初为什么要设计这个功能、这个技术问题有哪些优势、适用哪些场景又是另外回事了。 前者照猫画虎得其形,后者形神兼备得其意,这也是所谓青铜与王者的区别。 会使…

java使用回调函数

java回调函数 回调函数(callback Function),顾名思义就是用来回调的函数。在两个类A、B中,A在调用B接口的同时B也在调用A 回调函数也常用于线程中的异步获取消息。 举个简单的例子,公司中老板分发任务给员工A&#…

java中回调函数的实现

在java的事务中,有时候可能会遇到以下情况,第一步是更新某某表,中间可能要更新不确定的多步,最后一步是更新缓存,结构大致如下: (1)updateA(); (2)updateXX…

什么是java回调函数

回调函数 一:故事背景二:概念三:回调函数的作用四:java中如何进行回调4.1 类图4.2 定义回调接口4.3 实现回调接口4.4 调用方法使用回调函数4.5 Main函数调用4.6 总结描述 五:回调函数的优点5.1 灵活性5.2 解耦性5.3 异…

简单举例JAVA回调函数的实现

来自维基百科的对回调(Callback)的解释:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execut…

java回调函数机制

Java回调函数机制 参考了网上的一些资料,下面也做出一些总结,供初学者了解学习。 一、 概述 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调、异步调用 。 同步调用:一…

java中如何实现回调函数

最近工作需要研究了一会别人写的库,其中充满着各种"回调函数",因此把自己理解给记录下来,存档。 首先我们来看看回调函数 这个概念的具体由来,百度百科的示义如下: 回调函数就是一个通过函数指针调用的函数。…

Java回调函数 + 使用案例

文章目录 前言什么是回调函数第0个版本第1个版本第2个版本第3个版本第4个版本第5个版本第6个版本回头解析前言描述的问题1. MethodIntrospector.selectMethods()2. 抽象类MethodIntrospector3. 方法selectMethods()4. 成员变量USER_DECLARED_METHODS5. 方法doWithMethods()6. d…

Java-回调函数

什么是回调 函数调用可以分为三种模式,串行调用、异步调用、回调。这三种都是日常开发中常见到的方式。 一个方法执行完,再执行下一个,串行逻辑会阻塞线程执行流程,等到所有逻辑执行结束,线程才会结束。 异步执行是…

java回调函数(callBack)

最近有个新同事给我写了个接口,说是用到了回调,我等了半天发现结果才返回回来,把我都整急了。最后我看了他的代码,目瞪口呆。他还信誓旦旦的说没错,按网上的例子来写的。 我一搜,网上例子真一大堆&#xff…

java回调函数的作用以及运用

模块之间总是存在这一定的接口,从调用方式上看,可以分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,也是我们在写程序中经常使用的;回调是一种双向的调用模式,也就是说,被调用的…

Java回调函数理解和应用

#Java回调函数理解和应用 所谓回调:就是A类中调用B类中的某个方法C,然后B类中反过来调用A类中的方法D,D这个方法就叫回调方法,这样子说你是不是有点晕晕的。 在未理解之前,我也是一脸懵逼,等我理解之后&…

java回调函数(全干货)

产生接口回调的场景 产生接口回调的场景很简单,比如A叫B帮忙做一件事,交代完后A去忙别的事,然后B做完这件事之后会通知A, 通知A的这个动作就是接口回调的动作。接口回调 接口回调的意义是通过接口来实现解耦的的前提下调用另一个类的方法,也就是B为A准…

深入理解Java回调函数

废话不多说,像许多网上介绍回调机制的文章一样,我这里也以一个现实的例子开头:假设你公司的总经理出差前需要你帮他办件事情,这件事情你需要花些时间去做,这时候总经理肯定不能守着你做完再出差吧,于是就他…

java 回调函数解读

模块间调用 在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种: (1)同步调用 同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b()&…

java中的回调函数

CALLBACK,即回调函数,是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接…