.net core 浅克隆和深克隆/浅拷贝和深拷贝

 除非语言里只有基础类型,没有引用类型,否则用任何一种编程语言克隆对象都是很棘手的事情。

1、前言

"老沉,什么是深克隆,什么是浅克隆?"

“哈,迷茫了?这深深浅浅的体验是不是把你搞晕了?”

“嗯,这都是啥程序员黑话吗?”

“这是专业术语!因为有“引用类型”这个概念,所以才引申出来深克隆和浅克隆的名词。”

“我们先聊聊堆(Heap)和栈(Stack)吧。”

“好啊,老沉,听你说说!”

2、堆和栈

 堆:在.net 中准确的说是托管堆,它由 CLR 管理,当堆满了后,会自动清理垃圾,所以做.net开发,基本不需要关心内存的释放,原理还是需要了解的

另外,根据引用类型实例的大小,"堆"分为"GC堆"和"LOH(Large Object Heap)堆",当引用类型实例大小小于85000个字节的时候,实例被分配在"GC堆"上;当实例大小大于或等于于85000个字节的时候,实例被分配在"LOH(Large Object Heap)堆"。

:翻译起来应该是堆栈,因为老和堆放一块,感觉容易混,因此现在一般都简称为栈。

堆和栈是程序运行时,数据主要存放的两个存储区。

**堆区**存放引用类型的对象,主要由CLR/GC释放;

**栈区**存放函数的参数、局部变量、返回数据,其内存无需我们管理,也不接受GC管理,当栈元素被弹出后,立马释放。 

栈区大小在32位应用下有1MB大小,64位应用是4MB。为啥是这样的呢?看看下面这位`David Cutler`,就是他制定的规则,这是windwosNT系统的统一标配,和.net关系不大。

当程序的EXE 栈大小 或CreateThread()调用未明确指定堆栈大小时,它将选择一MB字节,几乎所有程序员都将其留给操作系统来选择大小。

当然1MB字节是很多的,一个真正的线程很少消耗超过几千字节的内存。因此,兆字节实际上是相当浪费的。并且由于Windows的内存机制,在按需分页的虚拟内存操作系统上您可以承受这种浪费,兆字节只是虚拟内存,只是处理器的编号,在实际寻址之前,您永远不会真正使用物理内存(机器中的RAM)。

在.net世界, 我们的程序一般不接受程序员自己分配栈空间,除非在不安全的模式使用stackalloc关键字。

也许你会觉得1MB有点小,其实真相恰恰是相反的,1MB已经太多了,这使得操作系统创建线程的能力大大降低,实际上 asp.net 可能只有 256KB~512KB之间,甚至于实际的使用可能只在 4KB左右,并且操作系统会倾向于进行优化,只提交你需要的堆栈大小。

windows在管理线程栈时的自动增加示意图。

通过`ILDASM`,查看PE头,可以看到实际已经分配的栈空间大小。

.net 运行时的栈提交方式也可以进行修改。

> 公共语言运行时的默认行为是在启动线程时提交完整的线程堆栈。如果必须在内存有限的服务器上创建大量线程,并且其中大多数线程将使用很少的堆栈空间,如果公共语言运行时未在线程执行完后立即提交完整的线程堆栈,则服务器的性能可能会更好。

<configuration>  <runtime>  <disableCommitThreadStack enabled="1" />  </runtime>  
</configuration>  

栈资源代表着什么呢?简言之,每个线程都需要1MB的栈大小,先不管其他的消耗,在windows 32位系统下, 因为只有2GB的内存地址空间,因此最多只能创建 2048 个线程,当然实际上会比这小。这样反推,windwos 64位操作系统也仅仅能创建 4096个线程。当然这个没考虑windows对栈提交的优化,如果它按需提交,那同样内存下,会支撑更多的线程创建。

因此如果要提升windwos创建线程的能力,需要降低默认的1MB的设定,当然有工具去修改这个设置,有兴趣的朋友可以去搜搜`Testlimit`。

为什么要从内存转移到栈或“加载”?另一方面,为什么要从栈转移到内存或“存储”呢?为什么不将它们全部都放在内存中呢?

因为简单!

因为从概念上讲,栈对于语言编译器编写者来说非常简单。栈是一种用于描述计算的简单易懂的机制。对于JIT编译器作者来说,栈在概念上也非常容易。使用栈是一种简化的抽象,因此,它又降低了我们的成本。

您问:“为什么要栈呢?” 为什么不直接将所有内存都耗尽?

好吧,让我们考虑一下。假设您要生成以下内容的CIL代码:

int x = A() + B() + C() + 10;

只有栈,则假设我们有一个约定,即“ add”,“ call”,“ store”等始终将其参数移出栈,并将其结果(如果有的话)放在栈上。要为此C#生成CIL代码,我们只需要说些类似的话:

load the address of x // The stack now contains address of x
call A()              // The stack contains address of x and result of A()
call B()              // Address of x, result of A(), result of B()
add                   // Address of x, result of A() + B()
call C()              // Address of x, result of A() + B(), result of C()
add                   // Address of x, result of A() + B() + C()
load 10               // Address of x, result of A() + B() + C(), 10
add                   // Address of x, result of A() + B() + C() + 10
store in address      // The result is now stored in x, and the stack is empty.

现在,我们将按照您的方式进行操作,其中每个操作码都将获取其操作数的地址以及将其结果存储到的地址:

Allocate temporary store T1 for result of A()
Call A() with the address of T1
Allocate temporary store T2 for result of B()
Call B() with the address of T2
Allocate temporary store T3 for the result of the first addition
Add contents of T1 to T2, then store the result into the address of T3
Allocate temporary store T4 for the result of C()
Call C() with the address of T4
Allocate temporary store T5 for result of the second addition
...

这是怎么回事吗?我们的代码越来越庞大,因为我们必须显式分配通常按照约定会放在栈上的所有临时存储。更糟糕的是,我们的操作码本身变得越来越庞大,因为它们现在都必须将要写入结果的地址以及每个操作数的地址作为参数。一条“ add”指令知道它将要从堆栈中取出两件事并放在一件事上,这可以是一个字节。一个带有两个操作数地址和一个结果地址的加法指令将非常庞大。

我们使用基于`栈的操作码`,因为栈可以解决常见的问题。即:我想分配一些临时存储,请尽快使用它,然后在完成后迅速删除它。假设我们有可用的栈,我们可以使操作码非常小,并使代码非常简洁。

3、值类型、引用类型

切入整体,bool 、byte 、char 、decimal 、double 、enum 、float 、int 、long 、sbyte 、short 、struct 、uint 、ulong 、ushort这些值类型都存储在栈内,而class 、interface 、delegate 、object 、string这些类型均存储在堆中。

对于引用类型,都会定义一个指针指向堆内存,因此在我们成为浅拷贝的时候,拷贝的实际是引用类型的指针。而值类型是直接拷贝的。

一个例子:

class Person
{public int Age { get; set; }public Person Father { get; set; }public Person Mother { get; set; }
}

如果我对该对象进行了浅克隆并更改了使用期限,则原始对象的使用期限将不会更改。

但是,如果我随后更改了克隆对象的父亲的属性,那么我也会影响原始对象的父亲,因为未克隆引用。

换一种方式思考,在C#中,当您对对象进行浅克隆时,您相比深克隆“浅一层”,因为变浅了,您要克隆的对象内的任何对象本身也不会递归地克隆。

深度克隆显然是相反的。它一直尝试克隆对象的所有属性,然后克隆该属性的属性。

4、成员克隆

如果您对C#中的克隆进行了研究,则可能遇到了“成员方式”克隆方法。它对每个类均可用,但“仅在该类内部”可用,因为它是Object的受保护方法。您不能在另一个类的对象上调用它。

class Person
{public string Name { get; set; }public Person Father { get; set; }public Person Mother { get; set; }public Person Clone(){return (Person)this.MemberwiseClone();}
}

然而,快速浏览一下智能感知就可以告诉我们一些…

创建当前对象的浅表副本。

因此,在此对象上调用clone只会对其进行浅克隆,而不会进行深克隆。

如果您的对象是纯粹的值对象,那么这实际上可以为您工作,您可以在这里停下来。但是在大多数情况下,我们正在寻找更深的克隆。

5、手动克隆

反正,你最了解你的类,不是吗?

class Person
{public string Name { get; set; }public Person Father { get; set; }public Person Mother { get; set; }public Person Clone(){return new Person{Name = this.Name,Father = this.Father == null ? null : new Person { Name = this.Father.Name },Mother = this.Mother == null ? null : new Person { Name = this.Mother.Name }};}
}

如果属性不多,不失为一个好办法。

6、二进制序列化器克隆

[Serializable]
class Person
{public string Name { get; set; }public Person Father { get; set; }public Person Mother { get; set; }public Person Clone(){IFormatter formatter = new BinaryFormatter();using (stream = new MemoryStream()){formatter.Serialize(stream, this);stream.Seek(0, SeekOrigin.Begin);return (Person)formatter.Deserialize(stream);}}
}

我们必须用[Serializable]属性来修饰我们的类,否则,我们会得到异常错误,这看起来不Nice!

当然你可以改良为一个静态方法

public static class CloningService
{public static T Clone<T>(this T source){// Don't serialize a null object, simply return the default for that objectif (Object.ReferenceEquals(source, null)){return default(T);}IFormatter formatter = new BinaryFormatter();using (stream = new MemoryStream()){formatter.Serialize(stream, source);stream.Seek(0, SeekOrigin.Begin);return (T)formatter.Deserialize(stream);}}
}

当然 [Serializable]标记别忘了。

7、json序列化克隆

public static class CloningService
{public static T Clone<T>(this T source){// Don't serialize a null object, simply return the default for that objectif (Object.ReferenceEquals(source, null)){return default(T);}var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };var serializeSettings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, serializeSettings), deserializeSettings);}
}

我只能说,每次必须这样做时,我都认为“将其转换为JSON并再次返回确实不是很好……”。

但这是可行的。它可以处理您扔给它的所有东西,而且几乎没有错。

**我认为,如果您正在寻找可靠的,可重用的方法来在代码中克隆对象,就是这个。**

8、json克隆的循环

眼尖的读者会注意到,在上述JSON克隆服务中,我们有一行处理引用循环。

这是非常非常普遍的情况,尤其是在用作DataModel的一部分的模型中,两个类将相互引用。无论您如何决定克隆对象,始终会遇到对象之间相互引用的问题,`并且任何克隆尝试都将陷入无休止的循环`。

因此,即使在上面的代码中,我们使用一个设置来解决它,也值得指出的是,无论您决定克隆对象的方式如何,这总是一个问题。

9、小结

亲爱的读者,你有什么高招?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/301145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

中国三代核电最新进展;AI助力发现系外行星;腾讯发现特斯拉系统漏洞;论文剽窃被撤销学位;金立破产债权达173亿;这就是今日大新闻...

今天是4月3日农历二月廿八今天星期三最近上班老是有人来推销办理信用卡是谁把我缺钱花的消息透露出去了下面是今天的大新闻中国发布三代核电最新进展&#xff08;环球时报&#xff09;由中国核能行业协会主办的中国核能可持续发展论坛1日在京举行&#xff0c;多位专家现场发布了…

java对象模型是什么_蓝石榴_个人博客_JVM内存结构、Java内存模型、Java对象模型...

JVM内存结构我们都知道&#xff0c;Java代码是要运行在虚拟机上的&#xff0c;而虚拟机在执行Java程序的过程中会把所管理的内存划分为若干个不同的数据区域&#xff0c;这些区域都有各自的用途。其中有些区域随着虚拟机进程的启动而存在&#xff0c;而有些区域则依赖用户线程的…

使用微信云托管快速部署一个.Net Core项目(一)

前几天微信将小程序开发者工具内的云托管升级为微信云托管&#xff0c;新增了很多诸如OpenApi、MySql数据库、流水线构建、web控制台等能力。看文档还是蛮激动的&#xff0c;对开发者来说确实是个好消息&#xff0c;因为之前的一些业务逻辑大都写在云函数或者部署在服务器里面&…

中国禁止洋垃圾后,韩国这座垃圾山已经烧了3个月,全部烧完还要5年!

全世界只有3.14 % 的人关注了数据与算法之美2017年5月&#xff0c;韩国洛东江畔的樱花&#xff08;图片来源&#xff1a;东方IC&#xff09;去年平昌冬奥会&#xff0c;由5名来自义城郡的姑娘组成的韩国冰壶队战胜各路强手&#xff0c;夺得亚军&#xff0c;这让义城郡这座风景秀…

zendframework配置篇

1. 重写配置 httpd.conf配置修改 LoadModule rewrite_module modules/mod_rewrite.so <Directory "D:\PHPProject\MVCA\public"> AllowOverride All </Directory> 2.application.config.php中添加模块&#xff0c;如 Album,紧跟Application后面

ADO.NET 2.0 中的新增 DataSet 功能

发布日期&#xff1a; 1/13/2005| 更新日期&#xff1a; 1/13/2005 Jackie Goldstein Renaissance Computer Systems 适用于&#xff1a; Microsoft ADO.NET 2.0 Visual Basic 编程语言 摘要&#xff1a;了解有关 .NET Framework DataSet 类以及与它密切相关的类中的新增 ADO…

为WPF播放GIF伤神不?

为WPF播放GIF伤神不&#xff1f;WpfAnimatedGif仓库地址&#xff1a;https://github.com/XamlAnimatedGif/WpfAnimatedGifNuget 包&#xff1a;WpfAnimatedGif[1]。今天介绍一个用于在 WPF 中显示动态 GIF 图片的库&#xff0c;可在 XAML 或代码中使用&#xff1a;WpfAnimatedG…

每日一笑 | 最真实的商业模式

全世界只有3.14 % 的人关注了数据与算法之美&#xff08;图源网络&#xff0c;侵权删&#xff09;

java执行更新sql_sql server执行更新需要更多时间

我有两个表(UserTable和UserProfile)和结构&#xff1a;create table userTable(id_user int identity(1,1) primary key ,Name varchar(300) not null ,Email varchar(500) not null ,PasswordUser varchar(700) not null,userType int ,constraint usertype_fk foreign key(u…

昨天订了一台FSC Lifebook S6220

准备出差用 转载于:https://www.cnblogs.com/rexhost/archive/2005/03/24/124683.html

如何绕过 TPM 2.0 安装 Windows 11 操作系统?

方法一&#xff1a;删除 appraiserres.dll 文件1.下载 Windows 11 原版系统 ISO 镜像文件并解压到单独文件夹。&#xff08;一条不成熟的建议&#xff1a;现在请直接拔掉网线或关闭 WiFi&#xff09;2.打开已解压的文件&#xff0c;点击【source】文件夹。3.选择【appraiserres…

《悦趣式连锁反应》玩转STEM教育!529块积木元件,N+1款炫酷模型

▲数据汪特别推荐点击上图进入玩酷屋之前推荐的“小小机械师”成了孩子最受欢迎的玩具&#xff0c;玩了的孩子几乎没有不喜欢的。还有家长买多套送给朋友的孩子作为生日礼物。有家长来问&#xff0c;还有没有这种类型的新产品。和厂家咨询后&#xff0c;小木找到了一款“小小机…

域名的MX设置及校验方法

国内私募机构九鼎控股打造APP&#xff0c;来就送 20元现金领取地址&#xff1a;http://jdb.jiudingcapital.com/phone.html内部邀请码&#xff1a;C8E245J &#xff08;不写邀请码&#xff0c;没有现金送&#xff09;国内私募机构九鼎控股打造&#xff0c;九鼎投资是在全国股份…

昨天去驾校桩考

我通过了&#xff0c;当然。 封闭的电子考场&#xff0c;在场外待考的人们不惜屈尊偷看场内的“考题”&#xff0c;不过想来也没有什么看的&#xff0c;不还是平常一样的正反手进出库和移库嘛。 8:30正式开考之前&#xff0c;可以买票&#xffe5;10练习一次&#xff0c;有一位…

java前补零工具类_java生成编码工具类,不足补0

~~~~~ 小小工具类&#xff01;你值得拥有简单粗暴&#xff0c;直接上代码import java.text.NumberFormat;/*** author: Abner* description: 编码工具类* date: Created on 2018/5/24*/public class SnUtils {/*** 生成 0000000000001 格式* param value 传入的数值* param min…

掌握神经网络,入门深度学习

从无人驾驶汽车到AlphaGo战胜人类&#xff0c;机器学习成为了当下最热门的技术。而机器学习中一种重要的方法就是深度学习。人工智能、机器学习与深度学习的关系深度学习的动机在于建立、模拟人脑进行分析学习的神经网络&#xff0c;整个过程就是使用多个处理层对数据进行高层抽…

[旧博客]Python 第一次

决定学习Python&#xff0c;发现其需要安装的东西很小&#xff0c;只需要在官方网站下载和15MB左右的安装包&#xff0c;Python for windows。安装完毕就双击IDE快捷方式就打开IDE了。 你很发现IDE好像很面熟&#xff0c;对啊&#xff0c;Python原生的IDE就和一个windows笔记本…

人造流星这种生日礼物,你有过吗?现在国外有了

全世界只有3.14 % 的人关注了数据与算法之美数据汪列举了不同“级别”的富豪是如何庆生的&#xff0c;大家看看自己处于什么水平。三流土豪的孩子过生日&#xff0c;看的是蜡烛...二流土豪的孩子过生日&#xff0c;看的是烟花...那么一流土豪的孩子看的又是什么呢&#xff1f;现…

Concurrent connection limit

这几天在做IIS 6上Web Service (WSE 2.0)的性能测试。在这个过程中陆续发现和解决了一些问题。 其中有一个问题比较有意思。我和项目组的同事发现&#xff0c;不论我们用C#写的模拟客户端用多少并发量来连接Web Service&#xff0c;服务器端监测到的并发连接数&#xff08;性能…

用啥Selenium?! .NET程序员就用自家的Playwright for .NET

提到用于Web应用程序自动化测试的工具&#xff0c;大家肯定第一个想到的就是Selenium。可以说&#xff0c;Selenium已经成为以编程方式自动化浏览器的事实标准。但是&#xff0c;Selenium作为老牌工具&#xff0c;也存在一些缺点&#xff0c;比如不支持Shadow DOM &#xff0c;…