zoukankan      html  css  js  c++  java
  • C#之MemberwiseClone与Clone

    MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

    为了实现深度复制,我们就必须遍历有相互引用的对象构成的图,并需要处理其中的循环引用结构。这无疑是十分复杂的。幸好借助.Net的序列化和反序列化机制,可以十分简单的深度Clone一个对象。原理很简单,首先将对象序列化到内存流中,此时对象和对象引用的所用对象的状态都被保存到内存中。.Net的序列化机制会自动处理循环引用的情况。然后将内存流中的状态信息反序列化到一个新的对象中。这样一个对象的深度复制就完成了。在原型设计模式中CLONE技术非常关键。

    下面的代码就是演示这个问题:

    using System;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace CloneDemo
    {
        [Serializable]
        class DemoClass
        {
            public int i = 0;
            public int[] iArr = { 1, 2, 3 };
    
            public DemoClass Clone1() //浅CLONE
            {
                return this.MemberwiseClone() as DemoClass;
            }
    
            public DemoClass Clone2() //深clone
            {
                MemoryStream stream = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, this);
                stream.Position = 0;
                return formatter.Deserialize(stream) as DemoClass;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                DemoClass a = new DemoClass();
                a.i = 10;
                a.iArr = new int[] { 8, 9, 10 };
                DemoClass b = a.Clone1();
                DemoClass c = a.Clone2();
    
                // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化 而 c不会变化
                  a.iArr[0] = 88;
    
                Console.WriteLine("MemberwiseClone");
                Console.WriteLine(b.i);
                foreach (var item in b.iArr)
                {
                    Console.WriteLine(item);
                }
    
                Console.WriteLine("Clone2");
                Console.WriteLine(c.i);
                foreach (var item in c.iArr)
                {
                    Console.WriteLine(item);
                }
    
                Console.ReadLine();
            }
        }
    }
    另外一个例子是针对数组,C#中的数组是引用型的变量,我们通过数组来进行演示:

    浅拷贝:

    using System;
    
    class ShallowCopy : ICloneable
    {
     public int[] v = {1,2,3};
    
     public Object Clone()
     {
     return this.MemberwiseClone();
      }
    
     public void Display()
     {
     foreach(int i in v)
          Console.Write( i + ", ");
        Console.WriteLine();
      }
    }
    
    class Client
    {
     public static void Main()
     {
        ShallowCopy sc1 = new ShallowCopy();
        ShallowCopy sc2 = (ShallowCopy)sc1.Clone();
        sc1.v[0] = 9;
    
        sc1.Display();
        sc2.Display();
      }
    }

    ShallowCopy对象实现了一个浅拷贝,因此当对sc1进行克隆时,其字段v并没有克隆,这导致sc1与sc2的字段v都指向了同一个v,因此,当修改了sc1的v[0]后,sc2的v[0]也发生了变化。

    深拷贝:

    using System;
    
    class DeepCopy : ICloneable
    {
     public int[] v = {1,2,3};
    
     // 默认构造函数
     public DeepCopy()
     {
      }
    
     // 供Clone方法调用的私有构造函数
     private DeepCopy(int[] v)
     {
     this.v = (int[])v.Clone();
      }
    
     public Object Clone()
     {
     // 构造一个新的DeepCopy对象,构造参数为
     // 原有对象中使用的 v 
     return new DeepCopy(this.v);
      }
    
     public void Display()
     {
     foreach(int i in v)
          Console.Write( i + ", ");
        Console.WriteLine();
      }
    }
    
    class Client
    {
     public static void Main()
     {
        DeepCopy dc1 = new DeepCopy();
        DeepCopy dc2 = (DeepCopy)dc1.Clone();
        dc1.v[0] = 9;
    
        dc1.Display();
        dc2.Display();
      }
    }

    这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的dc1与dc2不同。

    当然我们也可以建个深拷贝的帮助类:

    public static class ObjectCopier
    {
        /// <summary>
        /// Perform a deep Copy of the object.
        /// </summary>
        /// <typeparam name="T">The type of object being copied.</typeparam>
        /// <param name="source">The object instance to copy.</param>
        /// <returns>The copied object.</returns>
        public static T Clone<T>(T source)
        {
            if (!typeof(T).IsSerializable)
            {
                throw new ArgumentException("The type must be serializable.", "source");
            }
    
            // Don't serialize a null object, simply return the default for that object
            if (Object.ReferenceEquals(source, null))
            {
                return default(T);
            }
    
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T)formatter.Deserialize(stream);
            }
        }
    }    
  • 相关阅读:
    进程的实践与练习2
    士兵队列训练问题
    大数相加
    Ignatius and the Princess II
    Parentheses Balance (括号平衡)---栈
    简单计算器
    C++全排列函数next_permutation()和prev_permutation()
    黑白图像
    9*9乘法表
    输入5 个数按从小到大的顺序输出
  • 原文地址:https://www.cnblogs.com/ChineseMoonGod/p/6052365.html
Copyright © 2011-2022 走看看