摘要

该接口使你能够创建现有对象的副本的自定义的实现。该接口只提供了,一个Clone方法,实现对象的浅拷贝。有浅拷贝,那么就有相对应的深拷贝。但该接口并没有对我们提供,需要我们自己实现。

什么是浅拷贝与深拷贝?

浅拷贝

将对象的字段复制到新的对象副本中,同时将字段的值也复制过去,但引用类型值复制引用,而不是引用类型本身,也就是,如果源对象的引用类型的字段的值改变了,拷贝的对象的对应的引用类型的字段也会跟着变化。

深拷贝

将对象的字段复制到新的对象副本中,无论是值类型还是引用类型的字段,都会复制类型本身及值。但,源对象的值变化,并不会影响副本中的对应的值。

一个例子

浅拷贝

  1. using Newtonsoft.Json;
  2. using System;
  3. using System.IO;
  4. using System.Runtime.Serialization;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6.  
  7. namespace Wolfy.CloneDemo
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. Console.WriteLine("创建新对象:");
  14. Person p = new Person() { Name = "wolfy", Id = , Address = new Address { City = "北京", Details = "北京 海淀区" } };
  15. Console.WriteLine(p.ToString());
  16. Console.WriteLine("克隆对象:");
  17. Person p2 = (Person)p.Clone();
  18. Console.WriteLine(p2.ToString());
  19. var result = object.ReferenceEquals(p, p2);
  20. Console.WriteLine("p ReferenceEquals p2:" + result);
  21. Console.WriteLine("此时修改p对象,是否会影响p2对象?");
  22. p.Name = "wolfy2";
  23. p.Id = ;
  24. p.Address.City = "上海";
  25. Console.WriteLine("p:" + p.ToString());
  26. Console.WriteLine("p2:" + p2.ToString());
  27.  
  28. Console.Read();
  29. }
  30.  
  31. }
  32. [Serializable]
  33. class Person : ICloneable
  34. {
  35. /// <summary>
  36. /// 值类型
  37. /// </summary>
  38. private int _id;
  39. /// <summary>
  40. /// 特殊的引用类型
  41. /// </summary>
  42. private string _name;
  43. private Address _address;
  44. public int Id { get => _id; set => _id = value; }
  45. public string Name { get => _name; set => _name = value; }
  46. public Address Address { get => _address; set => _address = value; }
  47.  
  48. public override string ToString()
  49. {
  50. return JsonConvert.SerializeObject(this);
  51. }
  52. public object Clone()
  53. {
  54. /*
  55. MemberwiseClone方法创建的新对象,然后将当前对象的非静态字段复制到新的对象创建的浅表副本。
  56. 如果字段是值类型,则执行字段的按位复制。 如果字段是引用类型,引用将复制,但被引用的对象不;
  57. 因此,原始对象和其克隆引用同一对象。
  58. 例如,考虑对象称为 X 引用对象 A 和 B,反过来,引用对象 c。
  59. X 的浅表副本创建新的对象 X2 也引用对象 A 和 b。与此相反,
  60. X 的深层副本创建新对象 X2 引用 A2 和 B2,
  61. 是的一个副本的新对象并 B.B2,
  62. 反过来,引用新对象 C2,这是 C 的副本。该示例说明浅和深层复制操作之间的差异。
  63.  
  64. 有很多方法可以实现深层复制操作,如果浅表复制操作由MemberwiseClone方法并不满足你的需求。
  65. 这些要求包括:
  66.  
  67. 调用类构造函数要复制可以使用来自第一个对象的属性值创建第二个对象的对象。
  68. 这假定,对象的值完全由其类构造函数中定义。
  69.  
  70. 调用MemberwiseClone方法创建一个对象,
  71. 对象的浅表副本并将其值是与原始对象的任何属性或其值是引用类型的字段相同的新对象。
  72. DeepCopy方法在示例中演示了此方法。
  73.  
  74. 序列化对象是较深复制,,然后将序列化的数据还原到另一个对象变量。
  75.  
  76. 使用具有递归反射来执行深层复制操作。
  77. */
  78. return this.MemberwiseClone();
  79. }
  80. public object DeepClone()
  81. {
  82. using (Stream objectStream = new MemoryStream())
  83. {
  84. IFormatter formatter = new BinaryFormatter();
  85. formatter.Serialize(objectStream, this);
  86. objectStream.Seek(, SeekOrigin.Begin);
  87. return formatter.Deserialize(objectStream) as Person;
  88. }
  89. }
  90. }
  91. [Serializable]
  92. class Address
  93. {
  94. private string _city;
  95. private string _details;
  96.  
  97. public string City { get => _city; set => _city = value; }
  98. public string Details { get => _details; set => _details = value; }
  99. }
  100.  
  101. }

如上图所示,浅拷贝,值类型的并不会相互影响,但是引用类型的Address会跟着改变。浅拷贝,在对引用类型的字段时,会拷贝指向该对象的引用。

深拷贝

  1. Person p2 = (Person)p.Clone();

改为

  1. Person p2 = (Person)p.DeepClone();

结语

在实际项目中,这个接口自己是没用过。在看c#相关的文章的时候,看到了该接口的介绍,一篇文章了解下。如果非要说应用场景的话,比如,如果一个方法参数是一个引用类型,你需要在方法中对其进行操作,但又不想改变它原有的值,可以实现该接口,在clone实现深拷贝,对拷贝的副本进行操作,不影响它的原有的值,返回一个新的对象。

[c#基础]ICloneable接口的更多相关文章

  1. 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait

    [源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...

  2. [.net 面向对象编程基础] (16) 接口

    [.net 面向对象编程基础] (16) 接口 关于“接口”一词,跟我们平常看到的电脑的硬件“接口”意义上是差不多的.拿一台电脑来说,我们从外面,可以看到他的USB接口,COM接口等,那么这些接口的目 ...

  3. C#的System.ICloneable接口说明

    System.ICloneable接口支持克隆,即用与现有实例相同的值创建类的新实例.msdn上的解释很简单,主要就是clone方法的实行,介绍深拷贝和浅拷贝,搞的很糊涂,那么到底是什么意思呢?看看下 ...

  4. spring中基础核心接口总结

    spring中基础核心接口总结理解这几个接口,及其实现类就可以快速了解spring,具体的用法参考其他spring资料 1.BeanFactory最基础最核心的接口重要的实现类有:XmlBeanFac ...

  5. Go语言基础之接口

    Go语言基础之接口 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节. 接口 接口介绍 在Go语言中接口(interface)是一种类型,一种抽象的类 ...

  6. C#基础--类/接口/成员修饰符,多态、重载、重写,静态和非静态

    C#基础--类/接口/成员修饰符,多态.重载.重写,静态和非静态 类/接口/成员修饰符 C#修饰符---接口: 接口默认访问符是internal接口的成员默认访问修饰符是public C#修饰符--类 ...

  7. Java基础十--接口

    Java基础十--接口 一.接口的定义和实例 /* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 8 ...

  8. Java基础-面向接口(interface)编程

    Java基础-面向接口(interface)编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.接口的概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的“类 ...

  9. ICloneable接口 Clone 深拷贝 浅拷贝

    需要字段本身也实现了深拷贝Clone.应用场景不多,意义不大. 1. 隐含式地要求其子类和引用类也要实现ICloneable接口,如果引用层次比较深类似一个网状或树形接口,增加复杂性. 2. 考虑给s ...

随机推荐

  1. web.xml 部署描述符元素

    在每一个Web应用程序路径的WEB-INF/下和conf/下存在一个Web.xml配置文件,用来设定Web应用程序的配置.在Web.xml中的设定非常多,接下来分段来说明它的各项设定:<?xml ...

  2. Ex 6_20 最优二叉搜索树..._第六次作业

    假设关键字的总数为n,用c[i,j]表示第i个关键字到第j个关键字的最优二叉查找树的代价,我们的目标是求c[0,n-1].要求c[i,j],首先要从第i个关键字到第j个关键字中选一个出来作为根结点,选 ...

  3. laravel console - 自定义命令

    在改造一个支付流程,新的流程加入了一个新的数据表字段,但是这个新的字段需要通过计算来填充,所以为了兼容历史数据,必须将已有的数据行重新计算一遍该字段. 这时使用 laravel console 命令就 ...

  4. python 全栈开发,Day126(创业故事,软件部需求,内容采集,显示内容图文列表,MongoDB数据导入导出JSON)

    作业讲解 下载代码: HBuilder APP和flask后端登录 链接:https://pan.baidu.com/s/1eBwd1sVXTNLdHwKRM2-ytg 密码:4pcw 如何打开APP ...

  5. Django 关闭Debug后使用Nginx做静态文件的访问

    Django 关闭Debug后使用Nginx做静态文件的访问 关闭Django 的Debug参数 1 . 修改settings.py配置文件 DEBUG = False 2 . settings.py ...

  6. LINQ学习之旅(三)

    Linq to Sql语句之Join和Order By Join操作 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等.对各个表之间的关系,就用这些实现对多个表的操作. 说明:在Join ...

  7. OpenLdap的加密md5(Java+Python,同时提供明文-->密文,md5(名文)-->密文两种方法)

    # slappasswd -h {md5} -s "secret"{MD5}Xr4ilOzQ4PCOq3aQ0qbuaQ== import base64 import hashli ...

  8. hdu 1711( 模式串T在主串S中首次出现的位置)

    Sample Input213 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 1 313 51 2 1 2 3 1 2 3 1 3 2 1 21 2 3 2 1 Sample Out ...

  9. #10 [AH2017/HNOI2017]大佬

    题解: 题意看上去挺复杂的 分析一下就能发现自己的自信是没啥用的 只要随便dp一下看看最多能有多少天不使用增加自信 然后问题就变成了 求C1+C2+k=C 然后发现C有10^8 显然枚举C1是不行的了 ...

  10. 035 HDFS的联盟Federation

    一:概述 1.单个namenode的局限性 namespace的限制 单个namenode所能存储的对象受到JVM中的heap size的限制 namenode的扩张性 不可以水平扩张 隔离性 单个n ...