zoukankan      html  css  js  c++  java
  • 深度复制

    http://hi.baidu.com/jinghao1/blog/item/6e13271a199ba4dcad6e75e3.html

     
    查看文章
     
    c#浅度复制和深度复制
    2009-08-18 16:18

    简单来讲就是

    [转载http://blog.sina.com.cn/s/blog_4f84cd9c01008tbn.html]

    利用受保护的方法System.Object.MemberwiseClone()可以进行浅度复制,也就是说对于含有引用类型字段的对象来说,进行浅度复制意味着复制的对象和源对象存在这相同的引用。如果源对象的引用变量的值发生改变,那么被复制的对象的成员值也会发生变化。这是我们不希望看到的结果,所以我们有必要对对象进行深度复制。可以通过ICloneable接口的Clone方法实现,它通过创建新对象的方法进行深度复制。在比较复杂的对象系统调用Clone()是一个递归的过程。这次复制的对象和源对象是独立的。

    具体来讲【转载http://www.cnblogs.com/liaofang/articles/1288761.html

    申明:本文转载自Bear-Study-Hard

    从一个变量到另一个变量按值复制对象,而不是按引用复制对象(即以与结构相同的方式复制)可能非常复杂。因为一个对象可能包含许多对象的引用,例如字段、成员等,这将涉及许多烦人的处理操作。把每个成员从一个对象复制到另一个对象中可能不会成功,因为其中一些成员可能是引用类型。

    按照成员复制简单的对象可以通过派生于System.Object的MemberwiseClone()方法来完成,这是一个受保护的方法,但很容易在对象上定义一个该方法的公共方法。这个方法提供的复制功能称为阴影复制,因为它没有考虑引用类型成员。因此,新对象中的引用成员就会指向与源对象中相同成员的对象,在许多情况下这并不理想。如果要创建成员的新实例,此时应复制值,而不复制引用,就需要使用深度复制。

    我们看下面一个例子,其中有一个值类型的域:

        public class Cloner
        {
            public int Val;
            public Cloner(int newVal)
            {
                Val = newVal;
            }

            public object GetCopy()
            {
                return MemberwiseClone();
            }
        }

    我们用下面这段代码进行测试:

                Cloner mySource = new Cloner(5);
                Cloner myTarget = (Cloner)mySource.GetCopy();
                Console.WriteLine("mySource.Val = {0}",mySource.Val);
                Console.WriteLine("myTarget.Val = {0}",myTarget.Val);
                mySource.Val = 2;
                Console.WriteLine("mySource.Val = {0}",mySource.Val);
                Console.WriteLine("myTarget.Val = {0}",myTarget.Val);

    得出如下结果:


        我们发现,这种简单的值类型域的复制,即使改变源对象的值也不会影响复制对象的值。也就是说我们创建了一个新的对象实例。

    再来看一段修改的代码,使用引用类型的域作试验:

        public class Content
        {
            public int Val;
        }

        public class Cloner
        {
            public Content MyContent = new Content();
            public Cloner(int newVal)
            {
                MyContent.Val = newVal;
            }

            public object GetCopy()
            {
                return MemberwiseClone();
            }
        }

    这时,我们通过GetCopy()得到的阴影复制有一个域,它引用的对象与源对象相同。使用下面这段代码进行测试:

                Cloner mySource = new Cloner(5);
                Cloner myTarget = (Cloner)mySource.GetCopy();
                Console.WriteLine("mySource.MyContent.Val = {0}",mySource.MyContent.Val);
                Console.WriteLine("myTarget.MyContent.Val = {0}",myTarget.MyContent.Val);
                mySource.MyContent.Val = 2;
                Console.WriteLine("mySource.MyContent.Val = {0}",mySource.MyContent.Val);
                Console.WriteLine("myTarget.MyContent.Val = {0}",myTarget.MyContent.Val);

    则得到不同的执行结果:


        发现,当改变源对象mySource.MyContent.Val的引用类型域的值后通过阴影复制得到的对象myTarget的引用类型域的值同时发生了改变。这是因为mySource.MyContent引用了与myTarget.MyContent相同的对象实例。

    为了说明这个过程,需要执行深度复制。修改上面的GetCopy()方法就可以进行深度复制,但最好使用.NET Framework的标准方式。为此,执行ICloneable接口,该接口有一个方法Clone(),这个方法不带参数,返回一个对象类型,其签名和上面使用的GetCopy()方法相同。

    我们使用这个接口看相应的阴影复制:

        public class Content
        {
            public int Val;
        }

        public class Cloner : ICloneable
        {
            public Content MyContent = new Content();
            public Cloner(int newVal)
            {
                MyContent.Val = newVal;
            }

            #region ICloneable 成员

            public object Clone()
            {
                Cloner clonedCloner = new Cloner(MyContent.Val);
                return clonedCloner;
            }

            #endregion
        }

    测试代码为:

                Cloner mySource = new Cloner(5);
                Cloner myTarget = (Cloner)mySource.Clone();
                Console.WriteLine("mySource.MyContent.Val = {0}",mySource.MyContent.Val);
                Console.WriteLine("myTarget.MyContent.Val = {0}",myTarget.MyContent.Val);
                mySource.MyContent.Val = 2;
                Console.WriteLine("mySource.MyContent.Val = {0}",mySource.MyContent.Val);
                Console.WriteLine("myTarget.MyContent.Val = {0}",myTarget.MyContent.Val);

    得到如下结果:


        这里使用的是包含在源Cloner对象(MyContent)中的Content对象的Val字段去创建一个新的Cloner对象。这个域是一个值类型所以不需要进行深度复制。

    通过ICloneable.Clone方法支持除MemberwiseClone所提供的克隆之外的克隆。

    浅表副本(阴影复制)创建与原始对象具有相同类型的新实例,然后复制原始对象的非静态字段。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制该引用但不复制被引用的对象;这样,原始对象中的引用和复本中的引用指向同一个对象。相反,对象的深层副本(深度复制)复制对象中字段直接或间接引用的全部内容。

    例如,如果 X 是一个具有对对象 A 和对象 B 的引用的 Object,并且对象 A 还具有对对象 M 的引用,则 X 的浅表副本是对象 Y,而 Y 同样具有对对象 A 和对象 B 的引用。


        相反,X 的深层副本是对象 Y,而对象 Y 具有对对象 C 和对象 D 的直接引用以及对对象 N 的间接引用,其中 C 是 A 的副本,D 是 B 的副本,而 N 是 M 的副本。

    最后我们再举一个例子,请大家看如下代码:

            [STAThread]
            static void Main(string[] args)
            {
                // Creates an instance of MyDerivedClass and assign values to its fields.
                MyDerivedClass m1 = new MyDerivedClass();
                m1.age = 42;
                m1.name = "Sam";
                Console.WriteLine("MyBaseClass.CompanyName = {0}",MyBaseClass.CompanyName);
                Console.WriteLine("m1.age = {0}",m1.age);
                Console.WriteLine("m1.name = {0}",m1.name);

                // Performs a shallow copy of m1 and assign it to m2.
                MyDerivedClass m2 = (MyDerivedClass) m1.MemberwiseClone();
                Console.WriteLine("MyBaseClass.CompanyName = {0}",MyBaseClass.CompanyName);
                Console.WriteLine("m2.age = {0}",m2.age);
                Console.WriteLine("m2.name = {0}",m2.name);

                m1.age = 21;
                m1.name = "Dick";
                Console.WriteLine("MyBaseClass.CompanyName = {0}",MyBaseClass.CompanyName);
                Console.WriteLine("m1.age = {0}",m1.age);
                Console.WriteLine("m1.name = {0}",m1.name);
                Console.WriteLine("MyBaseClass.CompanyName = {0}",MyBaseClass.CompanyName);
                Console.WriteLine("m2.age = {0}",m2.age);
                Console.WriteLine("m2.name = {0}",m2.name);
            }
        }

        class MyBaseClass
        {
            public static string CompanyName = "My Company";
            public int age;
            public string name;
        }

    下面这个方法和其中的MyBaseClass是等效的:

        class MyBaseClass : ICloneable
        {
            public static string CompanyName = "My Company";
            public int age;
            public string name;

            #region ICloneable 成员

            public object Clone()
            {
                MyDerivedClass myDerivedClass = new MyDerivedClass();
                myDerivedClass.age = age;
                myDerivedClass.name = name;
                return myDerivedClass;
            }

            #endregion
        }

    只不过在调用的时候写成这样: MyDerivedClass m2 = (MyDerivedClass) m1. Clone();

    0
    0
  • 相关阅读:
    C语言格式化字符串细节 --- %*s %*c %*.*s
    ROC曲线与AUC计算总结
    python 数字列表排序,输出对应的索引 | 转载
    python 类间的有趣调用
    Linux下以16进制形式显示文件内容的方法
    一些书签
    椭圆曲线算数原理与实现
    中国剩余定理来解密RSA密文
    在GridView控件内文本框实现TextChanged事件,勾选复选框时 :textbox文本框可编辑,编辑文本框的数字后 总金额会重新计算并统计
    关于RDLC子报表添加参数 错误“本地报表处理期间出错 。值不能为空。 参数名:value” 错误解决方法
  • 原文地址:https://www.cnblogs.com/hl3292/p/2105156.html
Copyright © 2011-2022 走看看