C#之MemberwiseClone与Clone

来源:互联网 发布:mac上打不开app store 编辑:程序博客网 时间:2024/06/03 15:59

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);        }    }}    
 

//在windows phone 7里面,使用下面的方法进行深复制.

需要引入dll--System.Runtime.Serialization

 public static T DeepCopy<T>(T obj)        {            object retval;            using (MemoryStream ms = new MemoryStream())            {                DataContractSerializer ser = new DataContractSerializer(typeof(T));                ser.WriteObject(ms, obj);                ms.Seek(0, SeekOrigin.Begin);                retval = ser.ReadObject(ms);                ms.Close();            }            return (T)retval;        }

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 考研考砸了怎么办 好学生九宫格怎么办 考试时有不会题怎么办 孩子是个滚刀肉怎么办 初中审题不认真怎么办 孩子出错率高怎么办 一年级小孩不认真怎么办 旅游时孩子走失怎么办 小孩抄作业怎么办啊 鼻塞流涕老不好怎么办 二年级学生贪玩怎么办 一年级孩子做题不认真怎么办 纹身之后喝酒啦怎么办 股票爆仓散户怎么办 小学一年级孩子马虎怎么办 一年级孩子考试马虎怎么办 数学题会还做错怎么办 小孩学习不认真怎么办 又呆又笨怎么办 孩子做数学题马虎怎么办 起泛怎么办小偏方 儿童过敏起疙瘩怎么办 小孩身上起范怎么办 小孩数学很粗心怎么办 小孩数学考试粗心怎么办 孩子太粗心了怎么办 害怕和别人交流怎么办 三岁宝宝挑食怎么办 眼睛有吃马虎怎么办 一年级的孩子粗心怎么办 做考卷很粗心怎么办 孩子叛逆不听他怎么办 学生上课爱动怎么办 孩子不粗心仔细怎么办 脚痒的无法无天怎么办 站的太久脚肿了怎么办 孩子做题粗心大意怎么办 孩子做作业粗心大意怎么办 初中生没有学习兴趣怎么办 孩子没有学习兴趣怎么办 小孩没有学习兴趣怎么办