C#的System.ICloneable接口说明
System.ICloneable接口支持克隆,即用与现有实例相同的值创建类的新实例。msdn上的解释很简单,主要就是clone方法的实行,介绍深拷贝和浅拷贝,搞的很糊涂,那么到底是什么意思呢?看看下面的原理可能就会明白很多了。引自http://www.cnblogs.com/anderslly/archive/2007/04/08/implementingcloneabletype.html
原理
如果我们有两个值类型的变量,将其中一个变量的值赋给另一个,实际上会创建该值的一个副本,这个副本与原来的值没有什么关系——这意味着改变其中一 个的值不会影响另一个变量的值。而如果是两个引用类型的变量,其中一个变量的值赋给另一个的话(不包括string类型,CLR会对其有特殊处理),并没 有创建值的副本,而是使两个变量执行同一个对象——这意味着改变对象的值会同时影响两个变量。要真正地创建引用类型的副本,我们必须克隆(clone)变量指向的对象。
实现ICloneable接口使一个类型成为可克隆的(cloneable),这需要提供Clone方法来提供该类型的对象的副本。Clone方法不接受任何参数,返回object类型的对象(不管是何种类型实现该接口)。所以我们获得副本后仍需要进行显式地转换。
实现ICloneable接口的方式取决于我们的类型的数据成员。如果类型仅包含值类型(int,byte等类型)和string类型的数据成员, 我们只要在Clone方法中初始化一个新的对象,将其的数据成员设置为当前对象的各个成员的值即可。事实上,object类的 MemberwiseClone方法会自动完成该过程。
如果自定义类型包含引用类型的数据成员,必须考虑Clone方法是实现浅拷贝(shallow copy)还是深拷贝(deep copy)。浅拷贝是指副本对象中的引用类型的数据成员与源对象的数据成员指向相同的对象。而如果是深拷贝,则必须创建整个对象的结构,副本对象中的引用类型的数据成员与源对象的数据成员指向不同的对象。
浅拷贝是容易实现的,就是使用前面提到的MemberwiseClone方法。开发人员往往希望使用的类型能够实现深拷贝,但会发现这样的类型并不 多。这种情况在System.Collections命名空间中尤其常见,这里面的类在其Clone方法中实现的都是浅拷贝。这么做主要出于两个原因:
- 创建一个大对象的副本对性能影响较大;
- 通用的集合类型可能会包含各种各样的对象,在这种情况下实现深拷贝并不可行,因为集合中的对象并非都是可克隆的,另外还存在循环引用的情况,这会让深拷贝过程陷入死循环。
对于强类型的集合情况有所不同,因为它包含的元素是可控制的,此时深拷贝变得有用,同时也是可行的。例如System.Xml.XmlNode在其Clone方法中实现了深拷贝。
另外,如果需要克隆一个未实现ICloneable接口却是可序列化的对象,通常可以通过序列化和反序列化来达到克隆的效果。但要小心,序列化过程不一定会序列化所以数据成员。
代码
下面的代码示例描述了克隆的各种方法。简单的Employee类仅仅包含string和int类型的成员,所以使用object类型的 MemberwiseClone方法创建副本。Team类的Clone方法实现了深拷贝,它包含了一个Employee对象的集合,同时Team类提供了 一个私有的构造函数用以简化Clone方法的代码。构造函数的这种用法是简化克隆过程的一种常见方式。
{
public string Name;
public string Title;
public int Age;
// Simple Emplyee constructor
public Employee(string name, string title, int age)
{
Name = name;
Title = title;
Age = age;
}
public object Clone()
{
return MemberwiseClone();
}
public override string ToString()
{
return string.Format("{0} ({1}) - Age {2}", Name, Title, Age);
}
}
Team类代码:
{
public List<Employee> TeamMembers = new List<Employee>();
public Team()
{
}
private Team(List<Employee> members)
{
foreach (Employee e in members)
{
TeamMembers.Add(e.Clone() as Employee);
}
}
// Adds an Employee object to the Team.
public void AddMember(Employee member)
{
TeamMembers.Add(member);
}
// Override Object.ToString method to return a string representation of the team.
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Employee e in TeamMembers)
{
sb.AppendFormat(" {0}\r\n", e);
}
return sb.ToString();
}
// Implementation of ICloneable.Clone.
public object Clone()
{
return new Team(this.TeamMembers);
// the following code would create a shallow copy of the team.
//return MemberwiseClone();
}
}
测试代码:
Team team = new Team();
team.AddMember(new Employee("Anders", "Developer", 26));
team.AddMember(new Employee("Bill", "Developer", 46));
team.AddMember(new Employee("Steve", "CEO", 36));
Team clone = team.Clone() as Team;
// Display the original team.
Console.WriteLine("Original Team:");
Console.WriteLine(team);
// Display the cloned team.
Console.WriteLine("Clone Team:");
Console.WriteLine(clone);
// Make changes.
Console.WriteLine("*** Make a change to original team ***");
Console.WriteLine(Environment.NewLine);
team.TeamMembers[0].Title = "PM";
team.TeamMembers[0].Age = 30;
// Display the original team.
Console.WriteLine("Original Team:");
Console.WriteLine(team);
// Display the cloned team.
Console.WriteLine("Clone Team:");
Console.WriteLine(clone);
C#的System.ICloneable接口说明的更多相关文章
- 通过实现System.IComparable接口的CompareTo方法对两个类进行比较
假设现在有一个学生类 class Student { int age; public Student(int age) { this.age = age; } } 要使学生类之间能进行比较,实现Sys ...
- 消息队列接口API(posix 接口和 system v接口)
消息队列 posix API 消息队列(也叫做报文队列)能够克服早期unix通信机制的一些缺点.信号这种通信方式更像\"即时\"的通信方式,它要求接受信号的进程在某个时间范围内对信 ...
- ICloneable接口 Clone 深拷贝 浅拷贝
需要字段本身也实现了深拷贝Clone.应用场景不多,意义不大. 1. 隐含式地要求其子类和引用类也要实现ICloneable接口,如果引用层次比较深类似一个网状或树形接口,增加复杂性. 2. 考虑给s ...
- [c#基础]ICloneable接口
摘要 该接口使你能够创建现有对象的副本的自定义的实现.该接口只提供了,一个Clone方法,实现对象的浅拷贝.有浅拷贝,那么就有相对应的深拷贝.但该接口并没有对我们提供,需要我们自己实现. 什么是浅拷贝 ...
- FCL研究-集合- System.Collections 接口和对象集合
[目录] 发现自己已经有很长一段时间写代码没什么进步了,随便读读FCL的源码,看看之前一直用的方法是如何实现的,也顺便提高下自己.FCL很是庞大,很难下口,于是用最笨的办法,先看常见的命名空间,逐个展 ...
- 创作型---原型模式(C# ICloneable接口的实现)
在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的 ...
- c# System.Collections接口图
- .NET本质论 实例
对象和值的比较 CLR的类型系统(其实就是通用类型系统(CTS),它定义了如何在运行库中声明,使用和管理类型,同时也是运行库支持跨语言集成的一个重要组成部分)将对应简单值的类型同对应传统"对 ...
- [你必须知道的.NET]第二十七回:interface到底继承于object吗?
发布日期:2009.03.05 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 在.NET世界里,我们常常听到的一句话莫过于“S ...
随机推荐
- DOS 命令大全
MS DOS 命令大全 一.基础命令 1 dir 无参数:查看当前所在目录的文件和文件夹. /s:查看当前目录已经其所有子目录的文件和文件夹. /a:查看包括隐含文件的所有文件. /ah:只显示出隐含 ...
- 【boost】使用lambda表达式和generate_n生成顺序序列
程序中经常用到顺序序列(0,1,2,3,4,5,6.....),一直羡慕python有range这样的函数,而C++中通常只有用循环来处理这种初始化. 现在,结合boost库lambda(虽然差C++ ...
- 在Cubieboard上关闭irqbalance服务避免内存泄漏
十一一个假期回来,顺手看了看自己的cubieboard运行状态怎么样 aria2正常: btsync正常: samba正常: 很好, 顺手htop一下,已经开机了13天了,CPU使用率4%,内存使用率 ...
- ubuntu下apt-get update出现hash校验和错误
可能原因 校园网进行网络缓存导致内容滞后. 解决办法 先清除旧的apt-get更新列表 sudo rm -rf /var/lib/apt/lists/* 使用代理服务器或者VPN 重新更新 sudo ...
- air开发中的requestedDisplayResolution 扫盲
app.xml里面requestedDisplayResolution 取值可以为high/standard, 如果为high表示设备跟ios声明它需要使用高清屏(其实就是需要最大分辨率) 这里我猜测 ...
- Working with nil
[Working with nil] It’s always a good idea to initialize scalar variables at the time you declare th ...
- 用shell求两个文件的差集
假设有两个文件a.file和b.file,分别代表集合A和集合B. a.file的内容如下: abcde b.file的内容如下: cdefg 可以用grep命令 grep命令是常用来搜索文本内容的, ...
- JAVA网站高并发解决方案
一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构.性能的要求都很简单,随着互联网业务的不断丰富,网站 ...
- yum update Transaction Check Error
update系统时,发现其中一台server居然提示: Transaction Check Error:file /usr/lib/perl5/5.8.8/CGI.pm from install of ...
- C#制作艺术字
相信 Word 中的 艺术字 功能大家都不陌生, 前面这个 "Word" 单词就是它所为. 今天, 我们就利用C#来制作几款自己的艺术字, 可能会对我们了解字体图像的制作原理有一 ...