还有几天2008就过去了,在这提前祝大家在2009年一切顺利,财源滚滚!
在NET中,拷贝分为浅拷贝和深拷贝,
浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象(引用MSDN),对于这种的实现其实很简单,就是用Object类的MemberwiseClone方法。
深拷贝:创建一个新对象,这个新对象所包含的值和引用这些都是新的对象,也就是对该对象的所有东西进行完整的复制。MS对于这种方式基本上没有提供出实现,或者说根本就提供不了,因为这有很多因素来制约。不过它还是对少数的几个类提供了,例如DataTable的Copy方法....
"山寨文化",现在已经成为了流行的一个词,"山寨手机"、"山寨版明星"、连"春晚"也要山寨起来了.....!
既然现实环境有那么多的"山寨",那在程序的环境中,我们就更需要了!程序世界中的山寨就是对象的副本!MS只给了我们表层的复制,下面就让我们将"山寨"进行到底!
当我们要用到复制、粘贴、或撤消... 这些动作的时候,往往需要用到对象的副本进行操作,这样就需要去做深拷贝(WindowsForm开发中,这种情况是特别多的),但是麻烦的问题就出现在这里:我们可以实现ICloneable接口,然后手写Clone方法来达到我们的目的,可是,如果对象之间的关系比较多,对象中的属性又比较多的时候,那么对每个类写Clone这个方法的时候... 恐怖呀!这需要增加很多的开发时间,我们为什么不弄一个比较公用的方法呢?!
好的,方法出来了,其实就是做一个基类,里面有个共有的拷贝方法,每个想去深拷贝的类都要继承它。
再看下面BaseCopyObject的代码:
using System.Collections.Generic;
using System.Text;
using System.Reflection;
namespace CloneProject
{
public abstract class BaseCopyObject
{
#region ICloneable 成员
/// <summary>
/// 拷贝对象
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public virtual object CopyObject(params object[] obj)
{
object newObj = null;
Type mType = this.GetType();
ConstructorInfo[] constructArr = mType.GetConstructors();
//存在Public的构造函数
if (constructArr != null && constructArr.Length > 0)
{
newObj = Activator.CreateInstance(mType, this.GetConstructParams());
//获取实例字段
FieldInfo[] mFields = mType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (mFields != null && mFields.Length > 0)
{
foreach (FieldInfo info in mFields)
{
this.SetFieldValue(info, newObj, obj);
}
}
}
return newObj;
}
/// <summary>
/// 设置对象字段
/// </summary>
/// <param name="type"></param>
/// <param name="pValue"></param>
/// <param name="newObj"></param>
/// <param name="objs"></param>
private void SetFieldValue(FieldInfo field, object newObj, params object[] objs)
{
//事件字段
EventInfo ev = this.GetType().GetEvent(field.Name);
if (ev != null)
{
return;
}
Type mFieldType = field.FieldType;
object pValue = field.GetValue(this);
if (pValue != null)
{
//非值类型或者string类型
if (mFieldType != typeof(string) && !mFieldType.IsValueType)
{
//继承BaseCopyObjectType的类
if (this.IsBaseCopyObjectType(mFieldType))
{
//使用默认的方式COPY
if (!this.CopyDefineElement(mFieldType, ref pValue))
{
if (objs != null && objs.Length > 1)
{
//存在循环引用
if (pValue == objs[0])
{
pValue = objs[1];
}
else
{
pValue = ((BaseCopyObject)pValue).CopyObject(new object[] { this, newObj });
}
}
else
{
pValue = ((BaseCopyObject)pValue).CopyObject(new object[] { this, newObj });
}
}
}
else
{
this.CopyDefineElement(mFieldType, ref pValue);
}
}
field.SetValue(newObj, pValue);
}
}
#region 私有方法
private bool IsBaseCopyObjectType(Type type)
{
int i = -1;
IsBaseCopyObjectType(type.BaseType, ref i);
if (i == 1)
{
return true;
}
else
{
return false;
}
}
private void IsBaseCopyObjectType(Type type, ref int i)
{
if (type == typeof(Object))
{
i = 0;
}
else if (type == typeof(BaseCopyObject))
{
i = 1;
}
if (i == -1)
{
this.IsBaseCopyObjectType(type.BaseType, ref i);
}
}
#endregion
#region 虚方法
/// <summary>
/// 获取构造函数的参数
/// </summary>
/// <returns></returns>
protected virtual object[] GetConstructParams()
{
return null;
}
/// <summary>
/// 自定义复制处理
/// </summary>
/// <param name="type"></param>
/// <param name="obj"></param>
/// <returns></returns>
protected virtual bool CopyDefineElement(Type type, ref object obj)
{
return false;
}
#endregion
#endregion
}
}
基本思路:
1、先获取这个类型的Type,然后利用Activator.CreateInstance获取构造函数。
注意的地方:
因为对于继承BaseCopyObject的类,这些类的构造函数是否公开(Public),参数怎么定义对于这个方法来说是不确定的,所以在该方法对于构造函数做了两个动作
1、对于没有定义Public构造函数的类是复制不了的
2、this.GetConstructParams(),它是用来获取构造函数的参数数组的,子类可以根据自己的需要去定义构造函数接受的参数,默认是为Null的。
2、获取该类型的所有字段,mType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | dBindingFlags.Instance),然后通过FieldInfo的GetValue来获取值,SetValue来赋值,
注意的地方:
1、对于事件字段是不进行复制的,并且复制的时候会把它设置为null,因为对于事件来说,它的范围比较大,不可能对和它有关的对象都进行复制。
2、如果字段是值类型,很简单,直接GetValue和SetValue就可以了,不过对于引用类型有以下三种情况:
A、引用类型继承了BaseCopyObject,可以使用BaseCopyObject的CopyObject方法
pValue = ((BaseCopyObject)pValue).CopyObject(new object[] { this, newObj });
B、A类和B类之间是相互依赖关系的时候,例如a.B = b、并且b.A = a,a、b为A类和B类的对象,这种情况下,复制a对象的时候,对a对象中的所有字段进行复制,但是当复制到b对象的时候,会判断b对象中的字段是否有指回a对象的,如果有就不进行复制,而是赋引用,如果没有,就进行复制。
C、引用类型没有继承BaseCopyObject,例如,包含DataTable...这些对象,当对象中有这些引用的时候,这个基类的CopyObject是不能进行完全处理的,这就需要去重写CopyDefineElement这个方法,并且返回true,通过判断这个类中的字段类型来进行自定义的动作,从而达到自定义拷贝的目的。
3、在有些特殊的情况下,我们不想对某些字段的引用对象进行完整拷贝,这个时候我们可以使用重写CopyDefineElement这个方法(和上面2.C中的一样),达到我们自定义的目的
单元测试代码:
写完咯!在2008年中,看到了"山寨"开始成长,期望看到2009年"山寨"的前方到底是什么?!呵呵