** 一个克隆对象的 C#基类 **
By ** Amir Harel **
投递于 2002, 12, 30
** 摘要 ** ** : ** 一个实现 ICloneable
接口的类。
** 应用于: ** C#, .NET ** **
Download source files - 1.27 Kb
简介
虽然 在现实世界中的克隆课题是有争议的 , 在 .NET 世界使用它却足够安全 , 难道不是吗 ?
为实现一个类你究竟有多少次要实现ICloneable接口, 而且每一次都写相同的代码,或为每个类写特定的代码。而且,当你的类加入一个新的字段时,往往会忘记更新这个新字段的克隆方法。如果我没说错的话,这种时候往往会带来恼人的 bugs 。
这是我的类得以存在的原因。 藉由反射机制的小小帮助,我建立了一个用缺省行为实现了ICloneable接口的抽象类。现在或许你正在问自己: 什么是缺省行为? 那么我很高兴你这样询问。 克隆的缺省行为,是采用以下的规则来克隆类中的每一个字段:
- 查看一下类中的每一个字段是否支持
ICloneable
接口 - 如果某字段不支持
ICloneable
接口
,
那么该字段将以常规方式处理。这意味着,如果该字段是一个值类型,那么该值被拷贝;如果该字段是一个引用类型,克隆的字段将指向同一个对象。
- 如果该字段支持
ICloneable
接口
,
我们将使用其本身的
Clone
方法对其进行克隆。
- 如果该字段支持
IEnumerable
接口
,
我们需要检查他是否支持
IList 或 IDictionary 接口。
如果支持,那么我们迭代该集件 , 并且查看集合的每一项是否支持ICloneable
接口。
如何使用
让你的类支持
Icloneable
接口所要做的就是
,
将你的类继承自如下所述的
BaseObject
类
:
public class MyClass : BaseObject
{
public string myStr =”test”;
public int id;
}
public class MyContainer : BaseObject
{
public string name = “test2”;
public MyClass[] myArray= new MyClass[5];
public class MyContainer()
{
for(int i=0 ; i<5 ; i++)
{
this.myArray[I] = new MyClass();
}
}
}
现在在 Main 方法中加入如下代码:
static void Main(string[] args)
{
MyContainer con1 = new MyContainer();
MyContainer con2 = (MyContainer)con1.Clone();
con2.myArray[0].id = 5;
}
当监测
con2
实例时,你将会看到
MyClass
实例的第一项已经变为 5 ,而
con1
实例却没有改变。这样你将明白加入到类中的任意
支持 ICloneable
接口的 字段将被同样地克隆。而且,如果该字段支持
IList 或 IDictionary 接口,克隆方法将侦测该字段,轮询所有项,并同样地试图对他们进行克隆 。
实现
_///
1<summary>_
2
3
4 _
5
6
7 /// **BaseObject** 类是一个用来继承的抽象类。
8
9
10
11
12__
13
14
15 /// 每一个由此类继承而来的类将自动支持克隆方法。
16
17_ _
18_
19
20
21 _
22
23
24 /// 该类实现了Icloneable接口,并且每个从该对象继承而来的对象都将同样地
25
26
27 /// 支持Icloneable接口。
28
29
30
31
32__/// </summary>
_
public abstract class BaseObject : ICloneable
{
_///
1<summary>_
2
3
4 ___/// 克隆对象,并返回一个已克隆对象的引用_
5
6
7 _/// </summary>
_
_///
1<returns>_ _引用新的克隆对象 </returns>
_
__
public object Clone()
{
_ //首先我们建立指定类型的一个实例_
__
object newObject = Activator.CreateInstance( this.GetType() );
_ //我们取得新的类型实例的字段数组。_
__
FieldInfo[] fields = newObject.GetType().GetFields();
int i = 0;
foreach( FieldInfo fi in this.GetType().GetFields() )
{
_// 我们判断字段是否支持ICloneable接口。_
__
Type ICloneType = fi.FieldType.
GetInterface( "ICloneable" , true );
if( ICloneType != null )
{
_ //取得对象的Icloneable接口。_
__
__ ICloneable IClone = (ICloneable)fi.GetValue(this);
_ //我们使用克隆方法给字段设定新值。_
fields[i].SetValue( newObject , IClone.Clone() );
}
else
{
_ // 如果该字段部支持Icloneable接口,直接设置即可。_
__
fields[i].SetValue( newObject , fi.GetValue(this) );
}
_// 现在我们检查该对象是否支持IEnumerable接口,如果支持, _
_// 我们还需要枚举其所有项并检查他们是否支持IList 或 IDictionary 接口。_
__ Type IEnumerableType = fi.FieldType.GetInterface
( "IEnumerable" , true );
if( IEnumerableType != null )
{
_ //取得该字段的IEnumerable接口_
IEnumerable IEnum = (IEnumerable)fi.GetValue(this);
_ //这个版本支持IList 或 IDictionary 接口来迭代集合。_
__
__
Type IListType = fields[i].FieldType.GetInterface
( "IList" , true );
Type IDicType = fields[i].FieldType.GetInterface
( "IDictionary" , true );
int j = 0;
if( IListType != null )
{
_// 取得IList接口。_
__
IList list = (IList)fields[i].GetValue(newObject);
foreach( object obj in IEnum )
{
_ //查看当前项是否支持支持ICloneable 接口。_
__