zoukankan      html  css  js  c++  java
  • C# class 浅拷贝 与 深拷贝

    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);
        }
    }
    
    }
    复制代码
    转自:https://www.cnblogs.com/MRRAOBX/articles/4084171.html
  • 相关阅读:
    从零开始——PowerShell应用入门(全例子入门讲解)
    详解C# Tuple VS ValueTuple(元组类 VS 值元组)
    How To Configure VMware fencing using fence_vmware_soap in RHEL High Availability Add On——RHEL Pacemaker中配置STONITH
    DB太大?一键帮你收缩所有DB文件大小(Shrink Files for All Databases in SQL Server)
    SQL Server on Red Hat Enterprise Linux——RHEL上的SQL Server(全截图)
    SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)
    微软SQL Server认证最新信息(17年5月22日更新),感兴趣的进来看看哟
    Configure Always On Availability Group for SQL Server on RHEL——Red Hat Enterprise Linux上配置SQL Server Always On Availability Group
    3分钟带你了解PowerShell发展历程——PowerShell各版本资料整理
    由Find All References引发的思考。,
  • 原文地址:https://www.cnblogs.com/wangyu19900123/p/11126711.html
Copyright © 2011-2022 走看看