学无止境,精益求精

十年河东,十年河西,莫欺少年穷

学历代表你的过去,能力代表你的现在,学习代表你的将来

最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx

所谓深浅复制可解读为:

浅复制:在C#中调用 MemberwiseClone() 方法即为浅复制。如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则复制对象的引用,而不复制对象,因此:原始对象和其副本引用同一个对象!

深复制:如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则把引用类型的对象指向一个全新的对象!

上述的解释可能看不太懂,我们作如下案例进行分析:

  1. class Program
  2. {
  3. public static void Main()
  4. {
  5. //创建P1对象
  6. Person p1 = new Person();
  7. p1.Age = ;
  8. p1.Name = "Sam";
  9. p1.IdInfo = new IdInfo("");
  10.  
  11. //通过浅复制 得到P2对象
  12. Person p2 = p1.ShallowCopy();
  13. //分别输出
  14. Console.WriteLine("对象P1相关属性如下");
  15. DisplayValues(p1);
  16. //p1.Name = "";
  17. //p1.IdInfo.IdNumber = "XXXXX";
  18. Console.WriteLine("对象P2相关属性如下");
  19. DisplayValues(p2);
  20.  
  21. //现在测试深复制
  22. Person p3 = p1.DeepCopy();
  23.  
  24. p1.Name = "George";
  25. p1.Age = ;
  26. p1.IdInfo.IdNumber = "";
  27. Console.WriteLine("对象P1相关属性如下");
  28. DisplayValues(p1);
  29. //p1.IdInfo.IdNumber = "CCCCCCC";
  30. Console.WriteLine("对象P3相关属性如下");
  31. DisplayValues(p3);
  32. Console.Read();
  33. }
  34.  
  35. public static void DisplayValues(Person p)
  36. {
  37. Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
  38. Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
  39. }
  40. }
  41.  
  42. public class IdInfo
  43. {
  44. public string IdNumber;
  45.  
  46. public IdInfo(string IdNumber)
  47. {
  48. this.IdNumber = IdNumber;
  49. }
  50. }
  51.  
  52. public class Person
  53. {
  54. public int Age;
  55. public string Name;
  56. public IdInfo IdInfo;
  57.  
  58. public Person ShallowCopy()
  59. {
  60. return (Person)this.MemberwiseClone();
  61. }
  62.  
  63. public Person DeepCopy()
  64. {
  65. Person other = (Person)this.MemberwiseClone();
  66. other.IdInfo = new IdInfo(IdInfo.IdNumber);
  67. other.Name = String.Copy(Name);
  68. return other;
  69. }
  70. }

上述代码分析如下:

原始对象P1,通过浅复制得到对象P2,通过深复制得到P3

原始对象P1中的值类型属性有:Age 和 Name ,引用类型对象有:IdInfo

根据上述浅复制的概念可知:P2中的Age 和 Name 相对于 P1是全新的,但P2中的 IdInfo 和 P1中的 IdInfo 是同一个对象,二者同在一个内存地址!

根据上述深复制的概念可知:P3中的Age 和 Name 相对于 P1是全新的,但P3中的 IdInfo 和 P1中的 IdInfo 不是同一个对象,也就是说 P3中的IdInfo是一个全新的对象,开辟了自己的内存地址!

上述代码测试如下:

我们现在讲代码修改如下:

  1. public static void Main()
  2. {
  3. //创建P1对象
  4. Person p1 = new Person();
  5. p1.Age = ;
  6. p1.Name = "Sam";
  7. p1.IdInfo = new IdInfo("");
  8.  
  9. //通过浅复制 得到P2对象
  10. Person p2 = p1.ShallowCopy();
  11. //分别输出
  12. Console.WriteLine("对象P1相关属性如下");
  13. DisplayValues(p1);
  14. p1.Name = "浅复制中,修改了P1的Name属性,但Name是值类型,所以不会影响P2";
  15. p1.IdInfo.IdNumber = "浅复制中,修改了P1的IdInfo属性,但IdInfo是引用类型,所以会影响P2 (浅复制中引用类型原始对象和副本指向同一内存地址)";
  16. Console.WriteLine("对象P2相关属性如下");
  17. DisplayValues(p2);
  18.  
  19. Console.Read();
  20. }

在输出P2之前,我们修改了P1对象的值类型Name 和 引用类型 IdInfo 。

无论是浅复制还是深复制,副本中的值类型都是全新的!

浅复制中原始对象和副本的引用类型指向同一内存地址,所以,修改了P1的IdInfo会同时影响P2的IdInfo

输出如下:

继续修改代码,如下:

  1. public static void Main()
  2. {
  3. //创建P1对象
  4. Person p1 = new Person();
  5. p1.Age = ;
  6. p1.Name = "Sam";
  7. p1.IdInfo = new IdInfo("");
  8.  
  9. //现在测试深复制
  10. Person p3 = p1.DeepCopy();
  11.  
  12. p1.Name = "George";
  13. p1.Age = ;
  14. p1.IdInfo.IdNumber = "";
  15. Console.WriteLine("对象P1相关属性如下");
  16. DisplayValues(p1);
  17. p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
  18. Console.WriteLine("对象P3相关属性如下");
  19. DisplayValues(p3);
  20. Console.Read();
  21. }

深复制中原始对象和副本的引用类型指向各自的地址,两者完全是两个不同的对象!

因此:修改P1不会影响P3

so,是不是很简单,是不是很Easy.

深浅复制主要用于当创建一个对象需要消耗过多资源时,可以采取复制的方法提升效率!

大话设计模式的原话是这样滴:当你New一个对象时,每New一次,都需要执行一个构造函数,如果构造函数的执行时间很长,那么多次New对象时会大大拉低程序执行效率,因此:一般在初始化信息不发生变化的前提下,克隆是最好的办法,这既隐藏了对象的创建细节,又大大提升了性能!

当然,如果每个类都要写自己的深复制,这岂不是非常非常麻烦,因此,有一个通用的深复制方法,如下:

  1. /// <summary>
  2. /// 通用的深复制方法
  3. /// </summary>
  4. /// <typeparam name="T"></typeparam>
  5. [Serializable]
  6. public class BaseClone<T>
  7. {
  8. public virtual T Clone()
  9. {
  10. MemoryStream memoryStream = new MemoryStream();
  11. BinaryFormatter formatter = new BinaryFormatter();
  12. formatter.Serialize(memoryStream, this);
  13. memoryStream.Position = ;
  14. return (T)formatter.Deserialize(memoryStream);
  15.  
  16. }
  17. }

相关案例如下(通用的深复制方法使用时必须为相关类及类的引用类型加上可序列化标识:[Serializable]):

  1. class Program
  2. {
  3. public static void Main()
  4. {
  5. //创建P1对象
  6. Person p1 = new Person();
  7. p1.Age = ;
  8. p1.Name = "Sam";
  9. p1.IdInfo = new IdInfo("");
  10.  
  11. //现在测试深复制
  12. Person p3 = p1.Clone();
  13.  
  14. p1.Name = "George";
  15. p1.Age = ;
  16. p1.IdInfo.IdNumber = "";
  17. Console.WriteLine("对象P1相关属性如下");
  18. DisplayValues(p1);
  19. p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
  20. Console.WriteLine("对象P3相关属性如下");
  21. DisplayValues(p3);
  22. Console.Read();
  23. }
  24.  
  25. public static void DisplayValues(Person p)
  26. {
  27. Console.WriteLine(" Name: {0:s}, Age: {1:d}", p.Name, p.Age);
  28. Console.WriteLine(" Value: {0:d}", p.IdInfo.IdNumber);
  29. }
  30.  
  31. }
  32.  
  33. [Serializable]
  34. public class IdInfo
  35. {
  36. public string IdNumber;
  37.  
  38. public IdInfo(string IdNumber)
  39. {
  40. this.IdNumber = IdNumber;
  41. }
  42. }
  43.  
  44. [Serializable]
  45. public class Person : BaseClone<Person>
  46. {
  47. public int Age;
  48. public string Name;
  49.  
  50. public IdInfo IdInfo;
  51.  
  52. public Person ShallowCopy()
  53. {
  54. return (Person)this.MemberwiseClone();
  55. }
  56.  
  57. public Person DeepCopy()
  58. {
  59. Person other = (Person)this.MemberwiseClone();
  60. other.IdInfo = new IdInfo(IdInfo.IdNumber);
  61. other.Name = String.Copy(Name);
  62. return other;
  63. }
  64.  
  65. public override Person Clone()
  66. {
  67. MemoryStream memoryStream = new MemoryStream();
  68. BinaryFormatter formatter = new BinaryFormatter();
  69. formatter.Serialize(memoryStream, this);
  70. memoryStream.Position = ;
  71. return (Person)formatter.Deserialize(memoryStream);
  72. }
  73.  
  74. }
  75.  
  76. /// <summary>
  77. /// 通用的深复制方法
  78. /// </summary>
  79. /// <typeparam name="T"></typeparam>
  80. [Serializable]
  81. public class BaseClone<T>
  82. {
  83. public virtual T Clone()
  84. {
  85. MemoryStream memoryStream = new MemoryStream();
  86. BinaryFormatter formatter = new BinaryFormatter();
  87. formatter.Serialize(memoryStream, this);
  88. memoryStream.Position = ;
  89. return (T)formatter.Deserialize(memoryStream);
  90.  
  91. }
  92. }

@陈卧龙的博客

C# 深浅复制 MemberwiseClone的更多相关文章

  1. C#设计模式(6)——原型模式(Prototype Pattern) C# 深浅复制 MemberwiseClone

    C#设计模式(6)——原型模式(Prototype Pattern)   一.引言 在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创 ...

  2. C# Json反序列化 C# 实现表单的自动化测试<通过程序控制一个网页> 验证码处理类:UnCodebase.cs + BauDuAi 读取验证码的值(并非好的解决方案) 大话设计模式:原型模式 C# 深浅复制 MemberwiseClone

    C# Json反序列化   Json反序列化有两种方式[本人],一种是生成实体的,方便处理大量数据,复杂度稍高,一种是用匿名类写,方便读取数据,较为简单. 使用了Newtonsoft.Json,可以自 ...

  3. C# 深浅复制 MemberwiseClone(转载)

    最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/syst ...

  4. 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy

    读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...

  5. OC-深浅复制

    [OC学习-26]对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝 对象的拷贝分为浅拷贝和深拷贝, 浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址. 深 ...

  6. Objective-c 深浅复制

    深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...

  7. Python基础之列表深浅复制和列表推导式

    一.列表深浅复制: 浅拷贝内存图如下: 深拷贝内存图如下: 二.列表推导式: 实例: """ 列表推导式 练习:exercise01 """ ...

  8. 潭州课堂25班:Ph201805201 第五课:格式化输出和深浅复制 (课堂笔记)

    格式化输出和字符串转义 占位符 使用示意 作用 %s '%s %s' % ('hello', 'world') 表示占位的是str %d '%d %d' % (1, 2) 表示占位的是int %d ' ...

  9. 【Python初级】由判定回文数想到的,关于深浅复制,以及字符串反转的问题

    尝试用Python实现可以说是一个很经典的问题,判断回文数. 让我们再来看看回文数是怎么定义的: 回数是指从左向右读和从右向左读都是一样的数,例如1,121,909,666等 解决这个问题的思路,可以 ...

随机推荐

  1. 一条sql语句引发的遐想:select t.*, t.rowid from STUDENT t

    在学习oracle 过程当中,当在看tables时,比如STUDENT,右击——查看——查询,会自动有这样的一条查询语句: select t.*, t.rowid from STUDENT_TJB t ...

  2. 排错-LR安装No Background bmp defined in ...的解决办法

    LR安装No Background bmp defined in section General entry BGBmp的解决办法 by:授客 QQ:1033553122 问题描述: 我在win7装L ...

  3. 使用volley上传多张图片,一个参数对应多张图片,转载

    https://my.oschina.net/u/1177694/blog/491834 原帖地址 而如果使用volley的话,因为请求数据那些都很简便,但遇到上传文件就麻烦那可不好,同时使用多个网络 ...

  4. Android基础之6.0系统以上的权限分配

    public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle saved ...

  5. python常用模块之re模块(正则)

    python种的re模块常用的5种方法,分别是re.match   re.search  re.findall  re.split  re.sub. 在介绍五种方法之前,需要介绍一下正则的基础. . ...

  6. [20171120]理解v$session的state字段(11G).txt

    [20171120]理解v$session的state字段(11G).txt --//https://blogs.oracle.com/database4cn/vsession-%e4%bd%a0%e ...

  7. 给电脑插上无线网卡,变成路由器----Windows系统承载网络的使用

    1. 以管理员身份运行命令提示符(PowerShell) 2. 启用并设定虚拟wifi网卡 netsh wlan set hostednetwork mode=allow ssid=wifi名称 ke ...

  8. Can't debug c++ project because unable to static library start program *.lib

    Can't debug c++ project because unable to static library start program *.lib   I'm using a library ( ...

  9. 多个Activity和Intent

    Intent是Android应用程序组件之一,在Android系统当中表示一种意图,Intent中包含了一组信息: 最重要的内容是action(动作)和data(数据) Component name ...

  10. SQL update select结合语句详解及应用

    QL update select语句 最常用的update语法是: 1 2 UPDATE TABLE_NAME SET column_name1 = VALUE WHRER column_name2 ...