惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
酷 壳 – CoolShell
酷 壳 – CoolShell
人人都是产品经理
人人都是产品经理
Jina AI
Jina AI
雷峰网
雷峰网
博客园_首页
WordPress大学
WordPress大学
博客园 - 司徒正美
爱范儿
爱范儿
博客园 - 聂微东
IT之家
IT之家
美团技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 三生石上(FineUI控件)
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
博客园 - Franky
V
V2EX
GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志

博客园 - Kevin-moon

在IE中本地浏览与服务器浏览同一Html后,出现了奇异的结果 如何避免JQuery Dialog的内存泄露 JQuery另类视角-动态执行脚本的BUG JQuery另类视角-动态扩展对象 - Kevin-moon - 博客园 如何重构代码-简单、灵活的实现对象复制 如何重构代码-重构计划 如何重构代码-思路和方法 谈谈多线程的思维方式 简单的数据移动算法(C#) [ASP.NET MVC]-理解Routing 笑死人的十项大奖 深入APM,实现自己的APM [Windbg基础]-了解Symbols 深入线程,实现自定义的SynchronizationContext 重温ASP.NET的配置信息(简单、常用) 奇妙的SynchronizationContext 线程之间的通讯---SynchronizationContext 我真的就只能在远方看你吗?!!! "测试驱动开发(TDD)" 动态进行"自定义公式"计算
"对象"到"山寨对象"的完整转换
Kevin-moon · 2008-12-26 · via 博客园 - Kevin-moon

  还有几天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年"山寨"的前方到底是什么?!呵呵