秋天的梦想

 

如何实现深拷贝

 

1.深拷贝与浅拷贝的区别

拷贝即是通常所说的复制(Copy)或克隆(Clone),对象的拷贝也就是从现有对象复制一个“一模一样”的新对象出来。虽然都是复制对象,但是不同的复制方法,复制出来的新对象却并非完全一模一样,对象内部存在着一些差异。通常的拷贝方法有两种,即深拷贝和浅拷贝,那二者之间有何区别呢?MSDN里对IClone接口的Clone方法有这样的说明:在深层副本中,所有的对象都是重复的;而在浅表副本中,只有顶级对象是重复的,并且顶级以下的对象包含引用。可以看出,深拷贝和浅拷贝之间的区别在于是否复制了子对象。

浅拷贝(bitwise)正如其名就是2进制的拷贝,就是把对象里的值完全复制给了另一个对象。如 A = B; 就是把B中的成员变量的值完全给A拷贝了一份,就是栈内的拷贝。如果B中有一个成员变量指针已经申请了内存那A中的那个同样的成员变量也指向同一个块内存不会在堆内再申请一块内存。这样的问题就是 当B把内存释放了那A内的指针就是野指针了(也就是说这个指针是乱指的,有可能就会因为访问到危险的区域而导致程序崩溃)。

2.实现方式

通过挨个罗列的方式一次复制子对象是非常耗费人力的,如果子对象是引用类型,则还要需要考虑是否对子对象进一步深拷贝。

例如:下面Student的DeepCopy方法通过一次复制子对象实现了深拷贝,

实际应用中,一个类如果有几十个子对象,挨个复制对于开发人员来说索然无味比较费时费力。

 class Student : IDeepCopy, IShallowCopy
    {
        //为了简化,使用public 字段
        public string Name;
        public int Age;
        //自定义类型,假设每个Student只拥有一个ClassRoom
        public ClassRoom Room = new ClassRoom();

        public Student()
        {
        }
        public Student(string name, int age)
        {
            this.Name = name;
            this.Age = age;
        }
        public object DeepCopy()
        {
            Student s = new Student();
            s.Name = this.Name;
            s.Age = this.Age;
            s.Room = (ClassRoom)this.Room.DeepCopy();
            return s;
        }
        public object ShallowCopy()
        {
            return this.MemberwiseClone();
        }

        public override string ToString()
        {
            return "Name:" + Name + "\tAge:" + Age + "\t" + Room.ToString();
        }
    }

3.通过反射机制实现深拷贝

   public static class DeepCopyHelper
       {

        public static object Copy(this object obj)
        {
            Object targetDeepCopyObj;
            Type targetType = obj.GetType();
            //值类型
            if (targetType.IsValueType == true)
            {
                targetDeepCopyObj = obj;
            }
            //引用类型 
            else
            {
                targetDeepCopyObj = System.Activator.CreateInstance(targetType);   //创建引用对象 
                System.Reflection.MemberInfo[] memberCollection = obj.GetType().GetMembers();

                foreach (System.Reflection.MemberInfo member in memberCollection)
                {
                    if (member.MemberType == System.Reflection.MemberTypes.Field)
                    {
                        System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)member;
                        Object fieldValue = field.GetValue(obj);
                        if (fieldValue is ICloneable)
                        {
                            field.SetValue(targetDeepCopyObj, (fieldValue as ICloneable).Clone());
                        }
                        else
                        {
                            field.SetValue(targetDeepCopyObj, Copy(fieldValue));
                        }

                    }
                    else if (member.MemberType == System.Reflection.MemberTypes.Property)
                    {
                        System.Reflection.PropertyInfo myProperty = (System.Reflection.PropertyInfo)member;
                        MethodInfo info = myProperty.GetSetMethod(false);
                        if (info != null)
                        {
                            object propertyValue = myProperty.GetValue(obj, null);
                            if (propertyValue is ICloneable)
                            {
                                myProperty.SetValue(targetDeepCopyObj, (propertyValue as ICloneable).Clone(), null);
                            }
                            else
                            {
                                myProperty.SetValue(targetDeepCopyObj, Copy(propertyValue), null);
                            }
                        }

                    }
                }
            }
            return targetDeepCopyObj;
        }
    }

    public class ClassRoom
    {
        public string Name { set; get; }

        //.....以及其他一些属性

    }

    public class Student
    {
        const string school = "天朝小学";

        static string fee = "250";

        public string Name { set; get; }
        public ClassRoom Room { set; get; }
        //.....以及其他一些属性

        public string[] Lessions { set; get; }

    }

以下是测试结果:

clip_image002[13]

4.总结

通过反射机制比较节约代码量,但是相对耗费性能。如果你的逻辑运行在服务端,还是建议你人工复制,而在客户端,通过反射机制是一个不错的选择。

posted on 2012-02-15 14:00  秋天的梦想  阅读(978)  评论(0)    收藏  举报

导航