Kevin-moon

学习在于分享
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

"对象"到"山寨对象"的完整转换

Posted on 2008-12-26 09:04  Kevin-moon  阅读(3149)  评论(24编辑  收藏  举报

  还有几天2008就过去了,在这提前祝大家在2009年一切顺利,财源滚滚!

在NET中,拷贝分为浅拷贝和深拷贝,
  浅拷贝:创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象(引用MSDN),对于这种的实现其实很简单,就是用Object类的MemberwiseClone方法。
  深拷贝:创建一个新对象,这个新对象所包含的值和引用这些都是新的对象,也就是对该对象的所有东西进行完整的复制。MS对于这种方式基本上没有提供出实现,或者说根本就提供不了,因为这有很多因素来制约。不过它还是对少数的几个类提供了,例如DataTable的Copy方法....

  "山寨文化",现在已经成为了流行的一个词,"山寨手机"、"山寨版明星"、连"春晚"也要山寨起来了.....!
  既然现实环境有那么多的"山寨",那在程序的环境中,我们就更需要了!程序世界中的山寨就是对象的副本!MS只给了我们表层的复制,下面就让我们将"山寨"进行到底!
  当我们要用到复制、粘贴、或撤消... 这些动作的时候,往往需要用到对象的副本进行操作,这样就需要去做深拷贝(WindowsForm开发中,这种情况是特别多的),但是麻烦的问题就出现在这里:我们可以实现ICloneable接口,然后手写Clone方法来达到我们的目的,可是,如果对象之间的关系比较多,对象中的属性又比较多的时候,那么对每个类写Clone这个方法的时候... 恐怖呀!这需要增加很多的开发时间,我们为什么不弄一个比较公用的方法呢?!
  好的,方法出来了,其实就是做一个基类,里面有个共有的拷贝方法,每个想去深拷贝的类都要继承它。

  再看下面BaseCopyObject的代码:

using System;
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中的一样),达到我们自定义的目的


单元测试代码:

Code

 
  写完咯!在2008年中,看到了"山寨"开始成长,期望看到2009年"山寨"的前方到底是什么?!呵呵