博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 实现可克隆(ICloneable)的类型
阅读量:5248 次
发布时间:2019-06-14

本文共 3513 字,大约阅读时间需要 11 分钟。

<转自http://www.cnblogs.com/anderslly/archive/2007/04/08/implementingcloneabletype.html>

问题

有时需要创建一个自定义类型,它能为开发人员提供一种简单的机制来创建该类型实例的副本。

解决方案

实现System.ICloneable接口。

原理

如果我们有两个值类型的变量,将其中一个变量的值赋给另一个,实际上会创建该值的一个副本,这个副本与原来的值没有什么关系——这意味着改变其中一个的值不会影响另一个变量的值。而如果是两个引用类型的变量,其中一个变量的值赋给另一个的话(不包括string类型,CLR会对其有特殊处理),并没有创建值的副本,而是使两个变量执行同一个对象——这意味着改变对象的值会同时影响两个变量。要真正地创建引用类型的副本,我们必须克隆(clone)变量指向的对象

实现ICloneable接口使一个类型成为可克隆的(cloneable),这需要提供Clone方法来提供该类型的对象的副本。Clone方法不接受任何参数,返回object类型的对象(不管是何种类型实现该接口)。所以我们获得副本后仍需要进行显式地转换。

实现ICloneable接口的方式取决于我们的类型的数据成员。如果类型仅包含值类型(int,byte等类型)和string类型的数据成员,我们只要在Clone方法中初始化一个新的对象,将其的数据成员设置为当前对象的各个成员的值即可。事实上,object类的MemberwiseClone方法会自动完成该过程。

如果自定义类型包含引用类型的数据成员,必须考虑Clone方法是实现浅拷贝(shallowcopy)还是深拷贝(deepcopy)。浅拷贝是指副本对象中的引用类型的数据成员与源对象的数据成员指向相同的对象。而如果是深拷贝,则必须创建整个对象的结构,副本对象中的引用类型的数据成员与源对象的数据成员指向不同的对象。

浅拷贝是容易实现的,就是使用前面提到的MemberwiseClone方法。开发人员往往希望使用的类型能够实现深拷贝,但会发现这样的类型并不多。这种情况在System.Collections命名空间中尤其常见,这里面的类在其Clone方法中实现的都是浅拷贝。这么做主要出于两个原因:

  1. 创建一个大对象的副本对性能影响较大;
  2. 通用的集合类型可能会包含各种各样的对象,在这种情况下实现深拷贝并不可行,因为集合中的对象并非都是可克隆的,另外还存在循环引用的情况,这会让深拷贝过程陷入死循环。

对于强类型的集合情况有所不同,因为它包含的元素是可控制的,此时深拷贝变得有用,同时也是可行的。例如System.Xml.XmlNode在其Clone方法中实现了深拷贝。

另外,如果需要克隆一个未实现ICloneable接口却是可序列化的对象,通常可以通过序列化和反序列化来达到克隆的效果。但要小心,序列化过程不一定会序列化所以数据成员。

代码

下面的代码示例描述了克隆的各种方法。简单的Employee类仅仅包含string和int类型的成员,所以使用object类型的MemberwiseClone方法创建副本。Team类的Clone方法实现了深拷贝,它包含了一个Employee对象的集合,同时Team类提供了一个私有的构造函数用以简化Clone方法的代码。构造函数的这种用法是简化克隆过程的一种常见方式。

public
 
class
 Employee : ICloneable
{
    
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
 
class
 Team : ICloneable
{
    
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();
    }
}
测试代码:
//
 Create the original team.
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);

转载于:https://www.cnblogs.com/ChangingFond/p/7491474.html

你可能感兴趣的文章
svn“Previous operation has not finished; run 'cleanup' if it was interrupted“报错的解决方法...
查看>>
熟用TableView
查看>>
Java大数——a^b + b^a
查看>>
poj 3164 最小树形图(朱刘算法)
查看>>
服务器内存泄露 , 重启后恢复问题解决方案
查看>>
android一些细节问题
查看>>
KDESVN中commit时出现containing working copy admin area is missing错误提示
查看>>
利用AOP写2PC框架(二)
查看>>
【动态规划】skiing
查看>>
java定时器的使用(Timer)
查看>>
ef codefirst VS里修改数据表结构后更新到数据库
查看>>
boost 同步定时器
查看>>
[ROS] Chinese MOOC || Chapter-4.4 Action
查看>>
简单的数据库操作
查看>>
iOS-解决iOS8及以上设置applicationIconBadgeNumber报错的问题
查看>>
亡灵序曲-The Dawn
查看>>
Redmine
查看>>
帧的最小长度 CSMA/CD
查看>>
xib文件加载后设置frame无效问题
查看>>
编程算法 - 左旋转字符串 代码(C)
查看>>