• 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
  • 相关阅读:
    ASP.NET3.5 企业级项目开发 -- 第一章(续):企业级项目框架解决方案的提出
    ASP.NET安全问题--Forms验证(后篇)--实战篇
    ASP.NET安全问题--Froms验证的具体介绍(中篇)
    ASP.NET3.5 企业级项目开发 -- 第二章(续) 数据访问层(DAL)的开发解决方案提出
    ASP.NET3.5 企业级项目开发
    ASP.NET3.5 企业级项目开发 -- 第二章 数据访问层(DAL)的开发
    通过js的学习谈谈学习方法
    转载:LINQ to SQL更新数据库操作
    通用分页存储过程真的有注入漏洞吗?
    [MOSS开发]:webpart在部署时应该注意的地方
  • 原文地址:https://www.cnblogs.com/wangyu19900123/p/11126711.html
Copyright © 2020-2023  润新知