简介
虽然在现实世界中的克隆课题是有争议的 在NET世界使用它却足够安全 难道不是吗?
为实现一个类你究竟有多少次要实现ICloneable接口 而且每一次都写相同的代码或为每个类写特定的代码而且当你的类加入一个新的字段时往往会忘记更新这个新字段的克隆方法如果我没说错的话这种时候往往会带来恼人的bugs
这是我的类得以存在的原因 藉由反射机制的小小帮助我建立了一个用缺省行为实现了ICloneable接口的抽象类现在或许你正在问自己: 什么是缺省行为? 那么我很高兴你这样询问 克隆的缺省行为是采用以下的规则来克隆类中的每一个字段
·查看一下类中的每一个字段是否支持ICloneable接口
如果某字段不支持ICloneable接口那么该字段将以常规方式处理这意味着如果该字段是一个值类型那么该值被拷贝如果该字段是一个引用类型克隆的字段将指向同一个对象
如果该字段支持ICloneable接口我们将使用其本身的Clone方法对其进行克隆
如果该字段支持IEnumerable接口我们需要检查他是否支持IList 或 IDictionary 接口如果支持那么我们迭代该集件并且查看集合的每一项是否支持Cloneable接口
如何使用
让你的类支持Icloneable接口所要做的就是将你的类继承自如下所述的BaseObject类
public class MyClass : BaseObject
{
public string myStr =test;
public int id;
}
public class MyContainer : BaseObject
{
public string name = test;
public MyClass[] myArray= new MyClass[];
public class MyContainer()
{
for(int i= ; i< ; i++)
{
thismyArray[I] = new MyClass();
}
}
}
现在在Main方法中加入如下代码
static void Main(string[] args)
{
MyContainer con = new MyContainer();
MyContainer con = (MyContainer)conClone();
conmyArray[]id = ;
}
当监测con实例时你将会看到MyClass实例的第一项已经变为而con实例却没有改变这样你将明白加入到类中的任意支持ICloneable接口的字段将被同样地克隆而且如果该字段支持IList 或 IDictionary 接口克隆方法将侦测该字段轮询所有项并同样地试图对他们进行克隆
BaseObject类的完整实现代码
/// <summary>
/// BaseObject类是一个用来继承的抽象类
/// 每一个由此类继承而来的类将自动支持克隆方法
/// 该类实现了Icloneable接口并且每个从该对象继承而来的对象都将同样地
/// 支持Icloneable接口
/// </summary>
public abstract class BaseObject : ICloneable
{
/// <summary>
/// 克隆对象并返回一个已克隆对象的引用
/// </summary>
/// <returns>引用新的克隆对象</returns>
public object Clone()
{
//首先我们建立指定类型的一个实例
object newObject = ActivatorCreateInstance(thisGetType());
//我们取得新的类型实例的字段数组
FieldInfo[] fields = newObjectGetType()GetFields();
int i = ;
foreach (FieldInfo fi in thisGetType()GetFields())
{
//我们判断字段是否支持ICloneable接口
Type ICloneType = fiFieldTypeGetInterface(ICloneable true);
if (ICloneType != null)
{
//取得对象的Icloneable接口
ICloneable IClone = (ICloneable)fiGetValue(this);
//我们使用克隆方法给字段设定新值
fields[i]SetValue(newObject ICloneClone());
}
else
{
// 如果该字段部支持Icloneable接口直接设置即可
fields[i]SetValue(newObject fiGetValue(this));
}
//现在我们检查该对象是否支持IEnumerable接口如果支持
//我们还需要枚举其所有项并检查他们是否支持IList 或 IDictionary 接口
Type IEnumerableType = fiFieldTypeGetInterface(IEnumerable true);
if (IEnumerableType != null)
{
//取得该字段的IEnumerable接口
IEnumerable IEnum = (IEnumerable)fiGetValue(this);
Type IListType = fields[i]FieldTypeGetInterface(IList true);
Type IDicType = fields[i]FieldTypeGetInterface(IDictionary true);
int j = ;
if (IListType != null)
{
//取得IList接口
IList list = (IList)fields[i]GetValue(newObject);
foreach (object obj in IEnum)
{
//查看当前项是否支持支持ICloneable 接口
ICloneType = objGetType()GetInterface(ICloneable true);
if (ICloneType != null)
{
//如果支持ICloneable 接口
//我们用它李设置列表中的对象的克隆
ICloneable clone = (ICloneable)obj;
list[j] = cloneClone();
}
//注意如果列表中的项不支持ICloneable接口那么
//在克隆列表的项将与原列表对应项相同
//(只要该类型是引用类型)
j++;
}
}
else if (IDicType != null)
{
//取得IDictionary 接口
IDictionary dic = (IDictionary)fields[i]GetValue(newObject);
j = ;
foreach (DictionaryEntry de in IEnum)
{
//查看当前项是否支持支持ICloneable 接口
ICloneType = deValueGetType()
GetInterface(ICloneable true);
if (ICloneType != null)
{
ICloneable clone = (ICloneable)deValue;
dic[deKey] = cloneClone();
}
j++;
}
}
}
i++;
}
return newObject;
}
}