zoukankan      html  css  js  c++  java
  • 浅谈.net平台下深拷贝和浅拷贝

    在.net类库中,对象克隆广泛存在于各种类型的实现中,凡是实现了ICloneable接口的类型都具备克隆其对象实例的能力。所以本文讲述的深拷贝和浅拷贝也是在实现ICloneable接口的基础上进行的

    基本概念:

    浅拷贝:指对象的字段被拷贝,而字段引用的对象不会被拷贝,拷贝对象和原对象仅仅是引用名称有所不同,但是它们共用一份实体。对任何一个对象的改变,都会影响到另外一个对象。大部分的引用类型,实现的都是浅拷贝,引用类型对象之间的赋值,就是复制一个对象引用地址的副本,而指向的对象实例仍然是同一个。

    深拷贝:指对象的子段被拷贝,同时字段引用的对象也进行了拷贝。深拷贝创建的是整个源对象的结构,拷贝对象和原对象相互独立,不共享任何实例数据,修改一个对象不会影响到另一个对象。值类型之间的赋值操作,执行的就是深拷贝。

    基本概念之参考代码:

    class Program
        {
            static void Main(string[] args)
            {
                Student s1 = new Student("li", 23);

                //浅拷贝
                Student s2 = s1;
                s2.Age = 27;
                s1.ShowInfo();//li's age is 27

                //深拷贝
                int i = 12;
                int j = i;
                j = 22;
                Console.WriteLine(i);//12

                Console.Read();
            }
        }

        class Student
        {
            public string Name;
            public int Age;

            public Student(string name, int age)
            {
                Name = name;
                Age = age;
            }

            public void ShowInfo()
            {
                Console.WriteLine("{0}'s age is {1}", Name, Age);
            }
        }

    分析:

    在上例中,实例s2对s1进行了浅拷贝,对s2中的Age字段进行更改,继而影响实例s1中的Age字段。

    深拷贝中,仅仅是值类型间简单的赋值,对“j”做出的更改不会更改“i”的值。

    深浅拷贝的实现:

    public object Clone()
    {
    return this.MemberwiseClone();
    }

    MemberwiseClone:创建一个浅表副本。过程是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用对象。

    参考代码:

    class Program
        {
            static void Main(string[] args)
            {
                ClassA ca = new ClassA();
                ca.value = 88;
                ClassA ca2 = new ClassA();
                ca2 = (ClassA)ca.Clone();
                ca2.value = 99;
                Console.WriteLine(ca.value + "-----" + ca2.value);//88---99

                ClassB cb = new ClassB();
                cb.Member.value = 13;

                ClassB cb2 = (ClassB)cb.Clone();
                cb2.Member.value = 7;
                Console.WriteLine(cb.Member.value.ToString() + "------" + cb2.Member.value.ToString());//浅拷贝:7---7      深拷贝:13----7          

                Console.Read();
            }
        }

        public class ClassA : ICloneable
        {
            public int value = 0;

            public object Clone()
            {
                return this.MemberwiseClone();
            }
        }

        public class ClassB : ICloneable
        {
            public ClassA Member = new ClassA();

            public object Clone()
            { 
                //浅拷贝
                return this.MemberwiseClone();

                //深拷贝
                ClassB obj = new ClassB();
                obj.Member = (ClassA)Member.Clone();
                return obj;
            }
        }

    分析:

    上例中,ca2复制ca对象,实现了深度拷贝。结果如同代码中显示:ca2中值类型字段的改变并不影响ca中的字段。

    在类ClassB中,引用类型成员Member,如果用ClassA中的clone方法实现则仅仅实现的是浅拷贝,在上述参考代码中能够看出:对cb2的member的改变影响着cb。但是当使用参考代码中的深度拷贝后,对cb2的member的改变则不会影响着cb。

    在网上找到一个综合的例子,有对比的来进行解释深浅拷贝:

    实例1:

    public class Sex:ICloneable 
        {
            private string _PSex;
            public string PSex
            {
                set{ _PSex = value;}
                get { return _PSex; }
            }

            //public object Clone()
            //{
            //    return this.MemberwiseClone();
            //}
        }

        public class Person : ICloneable
        {

            private Sex sex = new Sex();
            public int aa = 3;

            public string pSex
            {
                set { sex.PSex = value; }
                get { return sex.PSex; }
            }
            private string _PName;
            public string PName
            {
                set { this._PName = value; }
                get { return this._PName; }
            }

            public void ShowPersonInfo()
            {
                Console.WriteLine("-------------------------");
                Console.WriteLine("Name:{0} Sex:{1}", _PName, this.pSex);
                Console.WriteLine("-------------------------");
                Console.WriteLine(this.aa);
            }
            //浅拷贝
            public object Clone()
            {
                return this.MemberwiseClone();
            }
            //深拷贝
            public object DeepClone()
            {
                Person newP = new Person();
                newP.PName = this._PName;
                newP.pSex = this.pSex;
                return newP;
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("原对象:");
                Person p = new Person();
                p.PName = "Lee";
                p.pSex = "男";

                p.ShowPersonInfo();//原对象:lee 男 3

                //浅拷贝        
                Person copy = (Person)p.Clone();
                //深拷贝
                Person dcopy = (Person)p.DeepClone();

                Console.WriteLine("修改后的原对象:");
                p.PName = "Zhao";
                p.pSex = "女";
                p.aa = 1;
                p.ShowPersonInfo();//zhao 女 1

                Console.WriteLine("修改后的浅拷贝对象:");
                copy.ShowPersonInfo();//lee 女 3

                Console.WriteLine("修改后的深拷贝对象:");
                dcopy.ShowPersonInfo();//lee 男 3

                Console.WriteLine("直接拷贝对象:");
                Person PP = p;
                PP.ShowPersonInfo();//zhao 女 1

                Console.ReadLine();
            }
        }

    分析:

    首先需指出,上例中在类Sex中,加入Clone方法和不加对实例中运算结果没有影响。

    类Person中,引用类型但却是string类型的PName字段,引用类型pSex字段,值类型aa。

    初始值:lee 男 3  (先进行深浅拷贝)

    修改值:zhao 女 1

    浅拷贝值:lee 女 3

    深拷贝值:lee 男 3

    直接拷贝值:赵 女 1

    结果:上述可以说是对深浅拷贝中经常遇到的几种类型做出总结和对比,相信在一番体悟后可以学到一些知识。

    实例2:

    class Program
        {
            static void Main(string[] args)
            {
                int[] numbers = { 2, 3, 4, 5 };
                int[] numbersCopy = new int[5];
                numbers.CopyTo(numbersCopy, 0);
                numbersCopy[2] = 0;

                int[] numbers1 = { 2, 3, 4, 5 };
                int[] numbersClone1 = (int[])numbers1.Clone();
                numbersClone1[2] = 0;

                Console.Write(numbers[2] + "---" + numbersCopy[2]);//4---0
                Console.Write(numbers1[2] + "---" + numbersClone1[2]);//4--0


                //数组的复制也就是引用传递,指向的是同一个地址
                int[] numbers2 = { 2, 3, 4, 5 };
                int[] numbers2Copy = numbers2;
                numbers2Copy[2] = 0;

                Console.Write(numbers2[2]);//0
                Console.Write(numbers2Copy[2]);//0

                Console.Read();
            }
        }

  • 相关阅读:
    Core Animation 文档翻译—附录C(KVC扩展)
    Core Animation 文档翻译—附录B(可动画的属性)
    Core Animation 文档翻译—附录A(Layer样貌相关属性动画)
    Core Animation 文档翻译 (第八篇)—提高动画的性能
    Core Animation 文档翻译 (第七篇)—改变Layer的默认动画
    Core Animation 文档翻译 (第六篇)—高级动画技巧
    Core Animation 文档翻译 (第五篇)—构建Layer的层次结构
    用Markdown快速排版一片文章
    Core Animation 文档翻译 (第四篇)—让Layer的content动画起来
    Core Animation 文档翻译(第三篇)—设置Layer对象
  • 原文地址:https://www.cnblogs.com/sjqq/p/6485179.html
Copyright © 2011-2022 走看看