C#中深拷贝和浅拷贝
一:概念
内存:用来存储程序信息的介质。
指针:指向一块内存区域,通过它可以访问该内存区域中储存的程序信息。(C#也是有指针的)
值类型:struct(整形、浮点型、decimal的内部实现都是struct)、enum、可空类型
引用类型:class、interface、delegate、数组、object、string。
二:深拷贝和浅拷贝的认识:
举个例子:
Myclass a=new Myclass();
Myclass b=a;
首先,深、浅拷贝与上面的语句并没有任何关系,要区分深、浅拷贝,关键要看“=”是如何重载的。
深拷贝:不管是值类型还是引用类型,都在内存中新建一块储存块,该储存块的内容(由b指向)与被拷贝的储存块(由a指向)的内容相同,并新建一个指针(b)指向该新建的储存块。所以对b的操作是不会影响a。
浅拷贝:对于值类型,拷贝一份值的副本;对于引用类型,拷贝一份指针的副本(该副本仍然指向原指针指向的内存块,即a、b指向同一块内存)。对b的值类型进行操作,不会影响a;对b的引用类型进行操作,会影响a。
对于浅拷贝,有一种奇怪的现象,即:若a中包含一个string类型的字段,对a进行浅拷贝,赋值给b,在b中改变这个string类型的字段,a中string类型字段不受影响。string类型明明是引用类型,按照上面的理解,修改b中的string字段,a中的string字段也该作出相应的改变,这是为什么呢?
我们不妨看看string的定义,会发现string被readonly关键字修饰了,即改变string的值时,就会重新分配内存空间。有兴趣的话可以了解一下“C#字符串不可变性”。
三:string
String是引用类型。究其原因,是因为string对象是不可变的,包括长度和其中任何字符都是不可以改变的。
关于不可变数据类型,请参考:https://www.cnblogs.com/mushroom/p/4373951.html
string 对象称为不可变的(只读),因为一旦创建了该对象,就不能修改该对象的值。有的时候看来似乎修改了,实际是string经过了特殊处理,每次改变值时都会建立一个新的string对象,变量会指向这个新的对象,而原来的还是指向原来的对象,所以不会改变。这也是string效率低下的原因。如果经常改变string的值则应该使用StringBuilder而不使用string。
在例子中str1=”ab”,这时在内存中就将“ab”存下来,如果再创建字符串对象,其值也等于“ab”,str2=”ab”,则并非再重新分配内存空间,而是将之前保存的“ab”的地址赋给str2的引用,这就能印证例子2中的结果。而当str1=”abc”其值发生改变时,这时检查内存,发现不存在此字符串,则重新分配内存空间,存储“abc”,并将其地址赋给str1,而str2依然指向“ab”的地址。可以印证例子中的结果。
String是引用类型,只是编译器对其做了特殊处理。
四:实例(原型模式)
1)浅拷贝:
声明一个将要被克隆的类 clsShallow 和它将要包含的引用类型成员clsRefSalary类,clsShallow包含 CompanyName(静态字符串)、Age(值类型)、EmployeeName(字符串)、EmpSalary(引用类型)四个成员。

- public class clsShallow
- {
- public static string CompanyName = "My Company";
- public int Age;
- public string EmployeeName;
- public clsRefSalary EmpSalary;
- public clsShallow CreateShallowCopy(clsShallow inputcls)
- {
- return (clsShallow)inputcls.MemberwiseClone();
- }
- }
- public class clsRefSalary
- {
- public clsRefSalary(int _salary)
- {
- Salary = _salary;
- }
- public int Salary;
- }

再看如何调用:

- static void Main()
- {
- // Creates an instance of clsShallow and assign values to its fields.
- clsShallow objshallow = new clsShallow();
- objshallow.Age = 25;
- objshallow.EmployeeName = "Ahmed Eid";
- // add the ref value to the objshallow
- clsRefSalary clsref = new clsRefSalary(1000);
- objshallow.EmpSalary = clsref;
- // Performs a shallow copy of m1 and assign it to m2.
- clsShallow m2 = objshallow.CreateShallowCopy(objshallow);
- m2.Age = 20;
- m2.EmployeeName = "jay";
- // then modify the clsref salary value to be 2000
- clsref.Salary = 2000;
- // so the m1 object salary value become 2000
- Console.WriteLine(m2 == objshallow);
- Console.WriteLine(objshallow.EmpSalary.Salary);
- Console.WriteLine(m2.EmpSalary.Salary);
- Console.WriteLine(objshallow.Age);
- Console.WriteLine(m2.Age);
- Console.WriteLine(objshallow.EmployeeName);
- Console.WriteLine(m2.EmployeeName);
- }

最后看运行结果:
根据结果我们进行分析:
浅拷贝:
1、浅拷贝创建了类的一个新实例。(由对象的同一性得知:如果两个引用如m2 、 objshallow指向同一个对象的实例,则(m2 == objshallow)为true,而结果显示为 false,所以m2 和objshallow分别指向不同的实例,而objshallow引用所指向的实例是新创建的。
2、对于对象(clsShallow)中的引用类型成员(clsRefSalary),引用被复制,但引用的实例没有被复制,即新创建实例中的引用成员(m2.EmpSalary)等于原实例的引用成员(objshallow.EmpSalary)。 因为改变 clsref.Salary 等于2000后,objshallow.EmpSalary 和m2.EmpSalary的Salary都改变了。其实可通过Console.WriteLine(objshallow.EmpSalary == m2.EmpSalary) 输出为true来解释。
3、值类型字段逐位复制到新实例。即改变m2.Age不影响objshallow.Age值。有人要问,string 为引用类型,为什么改变m2.EmployeeName = "jay"而objshallow.EmployeeName = "Ahmed Eid"没变呢?这是因为:字符串具有恒等性,一个字符串一旦被创建,我们就不能再将其变长、变短、或者改变其中的任何字符。
2) 深拷贝:
修改代码如下:

- static void Main()
- {
- clsDeep objdeep = new clsDeep();
- objdeep.Age = 25;
- objdeep.EmployeeName = "Ahmed Eid";
- // add the ref value
- clsRefSalary clsref = new clsRefSalary(1000);
- objdeep.EmpSalary = clsref;
- // Performs a shallow copy of m1 and assign it to m2.
- clsDeep m2 = objdeep.CreateDeepCopy(objdeep);
- // then modify the clsref salary value to be 2000
- clsref.Salary = 2000;
- // so the m1 object salary value become 2000
- int EmpSalary = objdeep.EmpSalary.Salary;
- m2.Age = 20;
- m2.EmployeeName = "jay";
- // then modify the clsref salary value to be 2000
- clsref.Salary = 2000;
- // so the m1 object salary value become 2000
- Console.WriteLine(m2 == objdeep);
- Console.WriteLine(objdeep.EmpSalary.Salary);
- Console.WriteLine(m2.EmpSalary.Salary);
- Console.WriteLine(objdeep.EmpSalary == m2.EmpSalary);
- Console.WriteLine(objdeep.Age);
- Console.WriteLine(m2.Age);
- Console.WriteLine(objdeep.EmployeeName);
- Console.WriteLine(m2.EmployeeName);
- }
- [Serializable]
- // serialize the classes in case of deep copy
- public class clsDeep
- {
- public static string CompanyName = "My Company";
- public int Age;
- public string EmployeeName;
- public clsRefSalary EmpSalary;
- public clsDeep CreateDeepCopy(clsDeep inputcls)
- {
- MemoryStream m = new MemoryStream();
- BinaryFormatter b = new BinaryFormatter();
- b.Serialize(m, inputcls);
- m.Position = 0;
- return (clsDeep)b.Deserialize(m);
- }
- }
- [Serializable]
- public class clsRefSalary
- {
- public clsRefSalary(int _salary)
- {
- Salary = _salary;
- }
- public int Salary;
- }

记得加命名空间
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
查看结果:
深拷贝:
1、深拷贝创建了类的一个新实例
2、对于实例中的引用类型成员,创建引用类型成员对象的新副本。 ( objshallow.EmpSalary != m2.EmpSalary)
3、值类型字段逐位复制到新实例。
其实,浅拷贝和深拷贝的本质区别是实例中的引用类型成员如何拷贝, 浅拷贝复制引用,深拷贝创建新实例。
注:无论浅、深拷贝,静态成员都不会复制,因为静态成员属于类成员。
3)深复制方法

- /// <summary>
- /// Perform a deep Copy of the object.
- /// </summary>
- /// <typeparam name="T">The type of object being copied.</typeparam>
- /// <param name="source">The object instance to copy.</param>
- /// <returns>The copied object.</returns>
- public static T Clone<T>(this T source)
- {
- if (!typeof(T).IsSerializable)
- {
- throw new ArgumentException("The type must be serializable.", "source");
- }
- // Don't serialize a null object, simply return the default for that object
- if (Object.ReferenceEquals(source, null))
- {
- return default(T);
- }
- IFormatter formatter = new BinaryFormatter();
- Stream stream = new MemoryStream();
- using (stream)
- {
- formatter.Serialize(stream, source);
- stream.Seek(0, SeekOrigin.Begin);
- return (T)formatter.Deserialize(stream);
- }
- }

C#中深拷贝和浅拷贝的更多相关文章
- python 中 深拷贝和浅拷贝的理解
在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解. 在python中,对象的赋值的其实就是对象的引用.也就是说,当创建一个对象,然后赋给另外一 ...
- Python中深拷贝与浅拷贝的区别
转自:http://blog.csdn.net/u014745194/article/details/70271868 定义: 在Python中对象的赋值其实就是对象的引用.当创建一个对象,把它赋值给 ...
- C++中深拷贝与浅拷贝
浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B.这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指 ...
- python中深拷贝与浅拷贝
# 1.浅拷贝(复制东西)a = [11,22,33] # 实际上是浅拷贝# 没有把这个变量的值赋进去,而是把另一个变量的地址拿过去了,就叫浅拷贝.b = a # print(id(a))# prin ...
- python中深拷贝和浅拷贝
python中所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝. 首先,对赋值操作我们要有以下认识: 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ). 修改不可变 ...
- 关于Python中深拷贝与浅拷贝的理解(一)---概念
import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy. ...
- iOS中深拷贝、浅拷贝和retain的区别
浅拷贝:浅拷贝是对object对象的指针拷贝,让指针指向同一块内存地址,“对象永远只有一个",浅拷贝使对象的引用计数器+1.代码如下: 可以看出不可变字符串的指针指向了同一地址,并没有重新开 ...
- Python中深拷贝与浅拷贝区别
浅拷贝, list值是可变的,str值不可变,只能重新赋值 a=b=c='wjx'print(a,b,c)c= 'jmy'#重新赋值了,所以内存分配了新的地址print(a,b,c)print(id( ...
- python 中深拷贝和浅拷贝的区别
通俗的理解,浅就是外面,深就是里面.浅拷贝的意思就是只拷贝外面的一层,深拷贝就是拷贝的里面的所有. 看两段代码: 元组: #!/usr/bin/env/python # -*-coding:utf-8 ...
随机推荐
- .net core An assembly specified in the application dependencied mainfest<****.json>was not found解决办法
最近在开发项目中,遇到了一个问题.在本机开发中部署到本机iis上或者本机控制台都没有问题,运行正常.当发布部署到服务器(windowsServer)中的时候一直运行不起来,用控制台也运行不起来,直接报 ...
- 利用bulk添加百万条数据,进行测试
(1)连接数据库 public static void BulkToDB(DataTable dt) { //数据库连接 SqlConnection sqlCon = new SqlConnectio ...
- OpenStack 数据库操作 demo
#!/usr/bin/env python from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine f ...
- LeetCode题解-147 对链表进行插入排序
对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...
- Unity色子的投掷与点数的获得(详解)
前几天需要一个色子的投掷并且获得朝上点数的Unity脚本,在网上找了很多,都是一个模子刻出来的. 对于2018版的我来说,网上找的都是很早就弃用了的老版本. 好不容易能运行了,结果并不理想,于是又突发 ...
- iOS —— GCD 详解
一.什么是GCD Grand Central Dispatch (强大的中枢调度器) ,是异步执行任务的技术之一.纯C语言,有很多强大的函数. 二.GCD的优势 (1)GCD是苹果公司为多核并行运算提 ...
- 【bzoj4712】洪水 动态dp
不难发现此题是一道动态$dp$题 考虑此题没有修改怎么做,令$f[i]$表示让以$i$为根的子树被覆盖的最小花费,不难推出$f[i]=min(\sum_{j∈son[i]} f[j],val[i])$ ...
- iOS 枚举讲解
枚举增强程序的可读性,用法上还是需要注意的 1.C语言的写法 enum XMPPReconnectFlags { kShouldReconnect = 1 << 0, // If set, ...
- PHP查找与搜索数组元素
in_array()函数 in_array()函数在一个数组汇总搜索一个特定值,如果找到这个值返回true,否则返回false.其形式如下: boolean in_array(mixed needle ...
- Java之集合(五)LinkedList
转载请注明源出处:http://www.cnblogs.com/lighten/p/7298017.html 1.前言 Java中另一个常见的list就是本章将要讲的LinkedList.ArrayL ...