C#之MemberwiseClone与Clone

简介: (转自:http://www.cnblogs.com/zhaojin/archive/2012/03/22/2411299.html)MemberwiseClone方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。

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);
        }
    }
}    
目录
相关文章
|
6月前
|
开发工具 git
git clone TimeOut 无法下载 git 设置代理
git clone TimeOut 无法下载 git 设置代理
78 0
|
6月前
|
存储 Unix 开发工具
15分钟了解Git对象和引用(2)
15分钟了解Git对象和引用
52 0
|
存储 安全 Shell
Git clone 克隆私有项目
Git clone 克隆私有项目
Git clone 克隆私有项目
|
开发工具 git Windows
文件过大无法git pull/git clone解决办法
由于公司无线限速, windows下拉代码会比较慢, 导致过大的仓库无法clone/pull下来, 可以尝试以下方法, 将一次拉取的size缩小, 然后再fetch 效率云中代码库过大时, 会有限制, 出现无法pull或者clone的情况, 如下图 首先以shallow模式克隆 例如:  git clone http://gaoyuan03_iwaimai.
5811 0
|
5月前
|
存储 前端开发 开发工具
git clone -mirror 和 git clone 的区别
git clone -mirror 和 git clone 的区别
|
9月前
|
安全 Java 编译器
Clone函数
Clone函数
110 0
|
9月前
|
算法 Linux 网络安全
超详细!linux环境git clone探坑录
超详细!linux环境git clone探坑录
370 0
|
10月前
|
开发工具 git Windows
使用git clone 遇见git did not exit cleanly (exit code 128)的个人解决方案
使用git clone 遇见git did not exit cleanly (exit code 128)的个人解决方案
225 0
使用git clone 遇见git did not exit cleanly (exit code 128)的个人解决方案
|
10月前
|
开发工具 git
git clone & git reset 补充
git clone & git reset 补充
49 0
|
11月前
|
开发工具 git
git clone 命令只下载了一个readme文件
git clone 命令只下载了一个readme文件
267 0