当前位置: 首页 > news >正文

C# 深浅复制 MemberwiseClone

http://www.cnblogs.com/chenwolong/p/MemberwiseClone.html

最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://msdn.microsoft.com/zh-cn/library/system.object.memberwiseclone.aspx 

所谓深浅复制可解读为:

浅复制:在C#中调用 MemberwiseClone() 方法即为浅复制。如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则复制对象的引用,而不复制对象,因此:原始对象和其副本引用同一个对象!

深复制:如果字段是值类型的,则对字段执行逐位复制,如果字段是引用类型的,则把引用类型的对象指向一个全新的对象!

上述的解释可能看不太懂,我们作如下案例进行分析:

复制代码
    class Program
    {
        public static void Main()
        {
            //创建P1对象
            Person p1 = new Person();
            p1.Age = 42;
            p1.Name = "Sam";
            p1.IdInfo = new IdInfo("081309207");

            //通过浅复制 得到P2对象
            Person p2 = p1.ShallowCopy();
            //分别输出
            Console.WriteLine("对象P1相关属性如下");
            DisplayValues(p1);
            //p1.Name = "";
            //p1.IdInfo.IdNumber = "XXXXX";
            Console.WriteLine("对象P2相关属性如下");
            DisplayValues(p2);

            //现在测试深复制
            Person p3 = p1.DeepCopy();
          
            p1.Name = "George";
            p1.Age = 39;
            p1.IdInfo.IdNumber = "081309208";
            Console.WriteLine("对象P1相关属性如下");
            DisplayValues(p1);
            //p1.IdInfo.IdNumber = "CCCCCCC";
            Console.WriteLine("对象P3相关属性如下");
            DisplayValues(p3);
            Console.Read();
        }

        public static void DisplayValues(Person p)
        {
            Console.WriteLine("      Name: {0:s}, Age: {1:d}", p.Name, p.Age);
            Console.WriteLine("      Value: {0:d}", p.IdInfo.IdNumber);
        }
    }

    public class IdInfo
    {
        public string IdNumber;

        public IdInfo(string IdNumber)
        {
            this.IdNumber = IdNumber;
        }
    }

    public class Person
    {
        public int Age;
        public string Name;
        public IdInfo IdInfo;

        public Person ShallowCopy()
        {
            return (Person)this.MemberwiseClone();
        }

        public Person DeepCopy()
        {
            Person other = (Person)this.MemberwiseClone();
            other.IdInfo = new IdInfo(IdInfo.IdNumber);
            other.Name = String.Copy(Name);
            return other;
        }
    }
复制代码

上述代码分析如下:

原始对象P1,通过浅复制得到对象P2,通过深复制得到P3

原始对象P1中的值类型属性有:Age 和 Name ,引用类型对象有:IdInfo

根据上述浅复制的概念可知:P2中的Age 和 Name 相对于 P1是全新的,但P2中的 IdInfo 和 P1中的 IdInfo 是同一个对象,二者同在一个内存地址!

根据上述深复制的概念可知:P3中的Age 和 Name 相对于 P1是全新的,但P3中的 IdInfo 和 P1中的 IdInfo 不是同一个对象,也就是说 P3中的IdInfo是一个全新的对象,开辟了自己的内存地址!

上述代码测试如下:

我们现在讲代码修改如下:

复制代码
        public static void Main()
        {
            //创建P1对象
            Person p1 = new Person();
            p1.Age = 42;
            p1.Name = "Sam";
            p1.IdInfo = new IdInfo("081309207");

            //通过浅复制 得到P2对象
            Person p2 = p1.ShallowCopy();
            //分别输出
            Console.WriteLine("对象P1相关属性如下");
            DisplayValues(p1);
            p1.Name = "浅复制中,修改了P1的Name属性,但Name是值类型,所以不会影响P2";
            p1.IdInfo.IdNumber = "浅复制中,修改了P1的IdInfo属性,但IdInfo是引用类型,所以会影响P2 (浅复制中引用类型原始对象和副本指向同一内存地址)";
            Console.WriteLine("对象P2相关属性如下");
            DisplayValues(p2);

            Console.Read();
        }
复制代码

在输出P2之前,我们修改了P1对象的值类型Name 和 引用类型 IdInfo 。

无论是浅复制还是深复制,副本中的值类型都是全新的!

浅复制中原始对象和副本的引用类型指向同一内存地址,所以,修改了P1的IdInfo会同时影响P2的IdInfo

输出如下:

继续修改代码,如下:

复制代码
        public static void Main()
        {
            //创建P1对象
            Person p1 = new Person();
            p1.Age = 42;
            p1.Name = "Sam";
            p1.IdInfo = new IdInfo("081309207");


            //现在测试深复制
            Person p3 = p1.DeepCopy();

            p1.Name = "George";
            p1.Age = 39;
            p1.IdInfo.IdNumber = "081309208";
            Console.WriteLine("对象P1相关属性如下");
            DisplayValues(p1);
            p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
            Console.WriteLine("对象P3相关属性如下");
            DisplayValues(p3);
            Console.Read();
        }
复制代码

深复制中原始对象和副本的引用类型指向各自的地址,两者完全是两个不同的对象!

因此:修改P1不会影响P3

so,是不是很简单,是不是很Easy.

深浅复制主要用于当创建一个对象需要消耗过多资源时,可以采取复制的方法提升效率!

大话设计模式的原话是这样滴:当你New一个对象时,每New一次,都需要执行一个构造函数,如果构造函数的执行时间很长,那么多次New对象时会大大拉低程序执行效率,因此:一般在初始化信息不发生变化的前提下,克隆是最好的办法,这既隐藏了对象的创建细节,又大大提升了性能!

当然,如果每个类都要写自己的深复制,这岂不是非常非常麻烦,因此,有一个通用的深复制方法,如下:

 

复制代码
    /// <summary>
    /// 通用的深复制方法
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public class BaseClone<T>
    {
        public virtual T Clone()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            return (T)formatter.Deserialize(memoryStream);

        }
    }
复制代码

相关案例如下(通用的深复制方法使用时必须为相关类及类的引用类型加上可序列化标识:[Serializable]):

复制代码
   class Program 
    {
        public static void Main()
        {
            //创建P1对象
            Person p1 = new Person();
            p1.Age = 42;
            p1.Name = "Sam";
            p1.IdInfo = new IdInfo("081309207");


            //现在测试深复制
            Person p3 = p1.Clone();

            p1.Name = "George";
            p1.Age = 39;
            p1.IdInfo.IdNumber = "081309208";
            Console.WriteLine("对象P1相关属性如下");
            DisplayValues(p1);
            p1.IdInfo.IdNumber = "深复制中,修改了P1的IdInfo属性,即使IdInfo是引用类型,也不会影响P3 (深复制中引用类型原始对象和副本分别指向不同的内存地址)";
            Console.WriteLine("对象P3相关属性如下");
            DisplayValues(p3);
            Console.Read();
        }

        public static void DisplayValues(Person p)
        {
            Console.WriteLine("      Name: {0:s}, Age: {1:d}", p.Name, p.Age);
            Console.WriteLine("      Value: {0:d}", p.IdInfo.IdNumber);
        }

     
    }

   [Serializable]
    public class IdInfo
    {
        public string IdNumber;

        public IdInfo(string IdNumber)
        {
            this.IdNumber = IdNumber;
        }
    }

   [Serializable]
    public class Person : BaseClone<Person>
    {
        public int Age;
        public string Name;
      
        public IdInfo IdInfo;

        public Person ShallowCopy()
        {
            return (Person)this.MemberwiseClone();
        }

        public Person DeepCopy()
        {
            Person other = (Person)this.MemberwiseClone();
            other.IdInfo = new IdInfo(IdInfo.IdNumber);
            other.Name = String.Copy(Name);
            return other;
        }

        public override Person Clone()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            return (Person)formatter.Deserialize(memoryStream);
        }

    }

    /// <summary>
    /// 通用的深复制方法
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public class BaseClone<T>
    {
        public virtual T Clone()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            return (T)formatter.Deserialize(memoryStream);

        }
    }
复制代码

转载于:https://www.cnblogs.com/tsql/p/10872623.html

相关文章:

  • poj_1741——树的分治
  • vue中使用axios等异步方法this指向的问题
  • 教你学会Suse启动cron的方法
  • 关于BETA、RC、ALPHA、Release、GA等版本号的意义
  • 学习总结
  • 第十二周助教总结
  • 搞不清楚的302、303和307返回码
  • Mysql高级查询
  • Android学习(二十一)OptionsMenu选项菜单
  • React 高阶组件(HOC)实践
  • CMMI的SG/GG概念区别与SP/GP概念的区别
  • python函数声明与调用
  • 一些前端真正常用的工具和网站(会经常更新)
  • jenkins定时构建时间设置
  • 最快的捷径就是脚踏实地
  • [分享]iOS开发-关于在xcode中引用文件夹右边出现问号的解决办法
  • Angular Elements 及其运作原理
  • JS函数式编程 数组部分风格 ES6版
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • React+TypeScript入门
  • Shell编程
  • vue 个人积累(使用工具,组件)
  • Vue2.x学习三:事件处理生命周期钩子
  • windows下使用nginx调试简介
  • 大数据与云计算学习:数据分析(二)
  • 搞机器学习要哪些技能
  • 计算机在识别图像时“看到”了什么?
  • 力扣(LeetCode)22
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 微信小程序开发问题汇总
  • 一道闭包题引发的思考
  • 一天一个设计模式之JS实现——适配器模式
  • 用Canvas画一棵二叉树
  • 7行Python代码的人脸识别
  • gunicorn工作原理
  • # 消息中间件 RocketMQ 高级功能和源码分析(七)
  • #if和#ifdef区别
  • (1)Android开发优化---------UI优化
  • (145)光线追踪距离场柔和阴影
  • (笔试题)分解质因式
  • (黑马出品_高级篇_01)SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式
  • (四)Linux Shell编程——输入输出重定向
  • (五)c52学习之旅-静态数码管
  • (转)shell调试方法
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .htaccess 强制https 单独排除某个目录
  • .NET Core/Framework 创建委托以大幅度提高反射调用的性能
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .Net6支持的操作系统版本(.net8已来,你还在用.netframework4.5吗)
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • @FeignClient 调用另一个服务的test环境,实际上却调用了另一个环境testone的接口,这其中牵扯到k8s容器外容器内的问题,注册到eureka上的是容器外的旧版本...
  • [ IO.File ] FileSystemWatcher
  • [APIO2015]巴厘岛的雕塑
  • [HTML]Web前端开发技术28(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页
  • [iHooya]2023年1月30日作业解析