一:概念

内存:用来存储程序信息的介质。

指针:指向一块内存区域,通过它可以访问该内存区域中储存的程序信息。(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(引用类型)四个成员。

  1. public class clsShallow
  2. {
  3. public static string CompanyName = "My Company";
  4. public int Age;
  5. public string EmployeeName;
  6. public clsRefSalary EmpSalary;
  7.  
  8. public clsShallow CreateShallowCopy(clsShallow inputcls)
  9. {
  10. return (clsShallow)inputcls.MemberwiseClone();
  11. }
  12. }
  13. public class clsRefSalary
  14. {
  15. public clsRefSalary(int _salary)
  16. {
  17. Salary = _salary;
  18. }
  19. public int Salary;
  20. }

再看如何调用:

  1. static void Main()
  2. {
  3. // Creates an instance of clsShallow and assign values to its fields.
  4. clsShallow objshallow = new clsShallow();
  5. objshallow.Age = 25;
  6. objshallow.EmployeeName = "Ahmed Eid";
  7. // add the ref value to the objshallow
  8. clsRefSalary clsref = new clsRefSalary(1000);
  9. objshallow.EmpSalary = clsref;
  10.  
  11. // Performs a shallow copy of m1 and assign it to m2.
  12. clsShallow m2 = objshallow.CreateShallowCopy(objshallow);
  13. m2.Age = 20;
  14. m2.EmployeeName = "jay";
  15.  
  16. // then modify the clsref salary value to be 2000
  17. clsref.Salary = 2000;
  18. // so the m1 object salary value become 2000
  19.  
  20. Console.WriteLine(m2 == objshallow);
  21. Console.WriteLine(objshallow.EmpSalary.Salary);
  22. Console.WriteLine(m2.EmpSalary.Salary);
  23. Console.WriteLine(objshallow.Age);
  24. Console.WriteLine(m2.Age);
  25. Console.WriteLine(objshallow.EmployeeName);
  26. Console.WriteLine(m2.EmployeeName);
  27. }

最后看运行结果:

根据结果我们进行分析:

浅拷贝:

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) 深拷贝:

修改代码如下:

  1. static void Main()
  2. {
  3. clsDeep objdeep = new clsDeep();
  4. objdeep.Age = 25;
  5. objdeep.EmployeeName = "Ahmed Eid";
  6.  
  7. // add the ref value
  8. clsRefSalary clsref = new clsRefSalary(1000);
  9. objdeep.EmpSalary = clsref;
  10.  
  11. // Performs a shallow copy of m1 and assign it to m2.
  12. clsDeep m2 = objdeep.CreateDeepCopy(objdeep);
  13.  
  14. // then modify the clsref salary value to be 2000
  15. clsref.Salary = 2000;
  16.  
  17. // so the m1 object salary value become 2000
  18. int EmpSalary = objdeep.EmpSalary.Salary;
  19. m2.Age = 20;
  20. m2.EmployeeName = "jay";
  21.  
  22. // then modify the clsref salary value to be 2000
  23. clsref.Salary = 2000;
  24. // so the m1 object salary value become 2000
  25. Console.WriteLine(m2 == objdeep);
  26. Console.WriteLine(objdeep.EmpSalary.Salary);
  27. Console.WriteLine(m2.EmpSalary.Salary);
  28. Console.WriteLine(objdeep.EmpSalary == m2.EmpSalary);
  29. Console.WriteLine(objdeep.Age);
  30. Console.WriteLine(m2.Age);
  31. Console.WriteLine(objdeep.EmployeeName);
  32. Console.WriteLine(m2.EmployeeName);
  33. }
  34.  
  35. [Serializable]
  36. // serialize the classes in case of deep copy
  37. public class clsDeep
  38. {
  39. public static string CompanyName = "My Company";
  40. public int Age;
  41. public string EmployeeName;
  42. public clsRefSalary EmpSalary;
  43. public clsDeep CreateDeepCopy(clsDeep inputcls)
  44. {
  45. MemoryStream m = new MemoryStream();
  46. BinaryFormatter b = new BinaryFormatter();
  47. b.Serialize(m, inputcls);
  48. m.Position = 0;
  49. return (clsDeep)b.Deserialize(m);
  50. }
  51. }
  52.  
  53. [Serializable]
  54. public class clsRefSalary
  55. {
  56. public clsRefSalary(int _salary)
  57. {
  58. Salary = _salary;
  59. }
  60. public int Salary;
  61. }

记得加命名空间

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

查看结果:

深拷贝:

1、深拷贝创建了类的一个新实例

2、对于实例中的引用类型成员,创建引用类型成员对象的新副本。 ( objshallow.EmpSalary != m2.EmpSalary)

3、值类型字段逐位复制到新实例

实,浅拷贝和深拷贝的本质区别是实例中的引用类型成员如何拷贝, 浅拷贝复制引用,深拷贝创建新实例。

注:无论浅、深拷贝,静态成员都不会复制,因为静态成员属于类成员。

3)深复制方法

  1. /// <summary>
  2. /// Perform a deep Copy of the object.
  3. /// </summary>
  4. /// <typeparam name="T">The type of object being copied.</typeparam>
  5. /// <param name="source">The object instance to copy.</param>
  6. /// <returns>The copied object.</returns>
  7. public static T Clone<T>(this T source)
  8. {
  9. if (!typeof(T).IsSerializable)
  10. {
  11. throw new ArgumentException("The type must be serializable.", "source");
  12. }
  13.  
  14. // Don't serialize a null object, simply return the default for that object
  15. if (Object.ReferenceEquals(source, null))
  16. {
  17. return default(T);
  18. }
  19.  
  20. IFormatter formatter = new BinaryFormatter();
  21. Stream stream = new MemoryStream();
  22. using (stream)
  23. {
  24. formatter.Serialize(stream, source);
  25. stream.Seek(0, SeekOrigin.Begin);
  26. return (T)formatter.Deserialize(stream);
  27. }
  28. }

C#中深拷贝和浅拷贝的更多相关文章

  1. python 中 深拷贝和浅拷贝的理解

    在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解. 在python中,对象的赋值的其实就是对象的引用.也就是说,当创建一个对象,然后赋给另外一 ...

  2. Python中深拷贝与浅拷贝的区别

    转自:http://blog.csdn.net/u014745194/article/details/70271868 定义: 在Python中对象的赋值其实就是对象的引用.当创建一个对象,把它赋值给 ...

  3. C++中深拷贝与浅拷贝

    浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B.这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指 ...

  4. python中深拷贝与浅拷贝

    # 1.浅拷贝(复制东西)a = [11,22,33] # 实际上是浅拷贝# 没有把这个变量的值赋进去,而是把另一个变量的地址拿过去了,就叫浅拷贝.b = a # print(id(a))# prin ...

  5. python中深拷贝和浅拷贝

    python中所谓浅拷贝就是对引用的拷贝,所谓深拷贝就是对对象的资源的拷贝. 首先,对赋值操作我们要有以下认识: 赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 ). 修改不可变 ...

  6. 关于Python中深拷贝与浅拷贝的理解(一)---概念

    import copy a = [1, 2, 3, 4, ['a', 'b']] #原始对象 b = a #赋值,传对象的引用 c = copy.copy(a) #对象拷贝,浅拷贝 d = copy. ...

  7. iOS中深拷贝、浅拷贝和retain的区别

    浅拷贝:浅拷贝是对object对象的指针拷贝,让指针指向同一块内存地址,“对象永远只有一个",浅拷贝使对象的引用计数器+1.代码如下: 可以看出不可变字符串的指针指向了同一地址,并没有重新开 ...

  8. Python中深拷贝与浅拷贝区别

    浅拷贝, list值是可变的,str值不可变,只能重新赋值 a=b=c='wjx'print(a,b,c)c= 'jmy'#重新赋值了,所以内存分配了新的地址print(a,b,c)print(id( ...

  9. python 中深拷贝和浅拷贝的区别

    通俗的理解,浅就是外面,深就是里面.浅拷贝的意思就是只拷贝外面的一层,深拷贝就是拷贝的里面的所有. 看两段代码: 元组: #!/usr/bin/env/python # -*-coding:utf-8 ...

随机推荐

  1. .net core An assembly specified in the application dependencied mainfest<****.json>was not found解决办法

    最近在开发项目中,遇到了一个问题.在本机开发中部署到本机iis上或者本机控制台都没有问题,运行正常.当发布部署到服务器(windowsServer)中的时候一直运行不起来,用控制台也运行不起来,直接报 ...

  2. 利用bulk添加百万条数据,进行测试

    (1)连接数据库 public static void BulkToDB(DataTable dt) { //数据库连接 SqlConnection sqlCon = new SqlConnectio ...

  3. OpenStack 数据库操作 demo

    #!/usr/bin/env python from sqlalchemy.orm import sessionmaker from sqlalchemy import create_engine f ...

  4. LeetCode题解-147 对链表进行插入排序

    对链表进行插入排序. 插入排序的动画演示如上.从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示). 每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中. 插 ...

  5. Unity色子的投掷与点数的获得(详解)

    前几天需要一个色子的投掷并且获得朝上点数的Unity脚本,在网上找了很多,都是一个模子刻出来的. 对于2018版的我来说,网上找的都是很早就弃用了的老版本. 好不容易能运行了,结果并不理想,于是又突发 ...

  6. iOS —— GCD 详解

    一.什么是GCD Grand Central Dispatch (强大的中枢调度器) ,是异步执行任务的技术之一.纯C语言,有很多强大的函数. 二.GCD的优势 (1)GCD是苹果公司为多核并行运算提 ...

  7. 【bzoj4712】洪水 动态dp

    不难发现此题是一道动态$dp$题 考虑此题没有修改怎么做,令$f[i]$表示让以$i$为根的子树被覆盖的最小花费,不难推出$f[i]=min(\sum_{j∈son[i]} f[j],val[i])$ ...

  8. iOS 枚举讲解

    枚举增强程序的可读性,用法上还是需要注意的 1.C语言的写法 enum XMPPReconnectFlags { kShouldReconnect = 1 << 0, // If set, ...

  9. PHP查找与搜索数组元素

    in_array()函数 in_array()函数在一个数组汇总搜索一个特定值,如果找到这个值返回true,否则返回false.其形式如下: boolean in_array(mixed needle ...

  10. Java之集合(五)LinkedList

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7298017.html 1.前言 Java中另一个常见的list就是本章将要讲的LinkedList.ArrayL ...