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

C# 创建型设计模式----原型模式

1、值类型与引用类型、深拷贝与浅拷贝。

在了解原型模式前得先对这四个知识点有些了解。我先简单介绍一下这四个知识点。

1.1 值类型与引用类型(C#仅有这两种数据类型)

值类型:

常见的值类型:int、long、short、byte、float、double、bool、char、Struct(用户建立的结构体通常是值类型的)、Nullable Types(这是一个特殊的值类型,表示一个正常值或者空,比如int?)

所谓值类型就是其能直接用来表示一个值。不需要实例化等操作(new),值类型变量声明后,不管是否已经赋值,编译器为其分配内存。

例如:

int a = 10;
int b = 20;

 值类型直接存储在内存(称之为栈(STACK),栈以LIFO访问,后进栈的数据先被访问,栈的大小是固定的,不是动态分配的,所以访问速度快)中,当把一个值赋值给另外一个变量时,其实是把变量的值复制给了新的变量,而不会改变原有值。

引用类型:

常见的引用类型包括类(class),接口(interface),数组(array),委托(delegate)等。

引用类型是存储数据的引用也就是内存地址,实际数据是存储在托管堆(Managed Heap)上,用new动态分配内存,由GC(垃圾回收器)释放。

例如:

MyClass my=new MyClass();

区别:

  1. 存储位置‌:

    • 值类型‌:直接存储数据,例如整数、浮点数、结构体等。值类型变量在赋值或传递参数时会进行值的复制‌。
    • 引用类型‌:存储的是对象的引用,而对象的数据存储在堆上。引用类型变量在赋值或传递参数时,传递的是引用‌。
  2. 内存管理‌:

    • 值类型‌:内存分配和释放由编译器自动处理,不需要手动管理内存‌。
    • 引用类型‌:需要手动进行内存管理,使用new关键字分配内存,并通过垃圾回收机制自动释放内存‌。
  3. 传递方式‌:

    • 值类型‌:作为参数传递给方法时,是将变量的副本传递给方法‌。
    • 引用类型‌:作为参数传递给方法时,传递的是引用,方法中对引用类型的变量进行的任何修改都会影响到原始对象‌。
  4. 可空性‌:

    • 值类型‌:可以是可空的,即可以赋予null值‌。
    • 引用类型‌:本身就是引用,可以直接赋予null值‌

1.2、深克隆与浅克隆

浅克隆(Shallow Clone)

浅克隆是指复制对象的所有值类型字段,而对于引用类型字段,只是复制其引用地址,而不是复制引用的对象本身。这意味着,如果对象中包含引用类型的字段,改变目标对象中引用类型字段的值将反映到原始对象中,因为它们指向的是同一个堆上的地址‌

这有两个类,我们对其进行一个浅拷贝

/// <summary>
/// 引用类型
/// </summary>
public class Other
{public int Id { get; set; }
}public class MyClass{/// <summary>/// 值类型/// </summary>public int age { get; set; }/// <summary>/// 引用类型/// </summary>public Other other { get; set; }/// <summary>/// 浅拷贝/// </summary>/// <returns></returns>public MyClass RetureCopy(){return (MyClass)this.MemberwiseClone();//C#提供的浅拷贝方法}}

上面这个MyClass里既有引用类型也有值类型,下面是拷贝 

 private void WTBtn_Click(object sender, EventArgs e){//实例化一个引用类型MyClass my = new MyClass();my.name = "张三";my.age = 12;var other = my;//直接赋值也是浅拷贝。var AA = my.RetureCopy();//浅拷贝。other.name = "李四";//因为是引用类型,所以给other赋值,my相应的值也会改变other.age = 15;}

 我们在赋值前打个断点看一下拷贝的AA对象的值发现完全等于my

 然后继续改变AA的值发现怎么my的age值没有改变,但是里面的引用类型other的Id值跟着变成51了。

由此可以看出浅拷贝下,对象中包含引用类型的字段,改变目标对象中引用类型字段的值将反映到原始对象中。这是因为浅拷贝的引用类型仅拷贝的是内存地址,虽然是两个对象中不同的other对象,但其根本是指向同一个值的。

这里需要说明一下直接赋值的情况:如: var AA = my;这种也类似浅拷贝。其根本就是将my的内存地址赋给AA。所以改变AA里的所有字段,my的字段值也会跟着改变。

深克隆(Deep Clone)

深克隆不仅复制对象的所有值类型字段还会复制引用类型字段所指向的对象。这样,深拷贝后的对象与源对象完全独立,其中一个对象的改动不会影响到另一个对象。例如,如果有一个包含引用类型字段的对象,深克隆会复制这个引用类型字段所指向的对象,而不是仅仅复制引用地址‌。

还是以这两个类举例(改变一些东西,对照上面的看深拷贝与浅拷贝的区别): 

 /// <summary>/// 引用类型/// </summary>[Serializable]public class Other{public int Id { get; set; }}[Serializable]//表示此类可以序列化,深克隆必须声明该特性public class MyClass{/// <summary>/// 值类型/// </summary>public int age { get; set; }/// <summary>/// 引用类型/// </summary>public Other other { get; set; }/// <summary>/// 浅拷贝/// </summary>/// <returns></returns>public MyClass RetureCopy(){return (MyClass)this.MemberwiseClone();//C#提供的浅拷贝方法}/// <summary>/// 深克隆(该方法可写到其他地方)/// </summary>/// <typeparam name="T"></typeparam>/// <param name="obj"></param>/// <returns></returns>public  T DeepClone<T>(T obj){using (var memoryStream = new MemoryStream()){var formatter = new BinaryFormatter();formatter.Serialize(memoryStream, obj);memoryStream.Position = 0;return (T)formatter.Deserialize(memoryStream);}}}

进行深拷贝,可以看出对AA赋值再也影响不到my对象:

 private void WTBtn_Click(object sender, EventArgs e){//实例化一个引用类型MyClass my = new MyClass();my.other = new Other();my.other.Id = 21;my.age = 12;var AA = my.DeepClone<MyClass>(my);//深拷贝。//虽然other是引用类型,但深拷贝后无法影响原来的对象AA.age = 15;AA.other.Id = 51;}

通过对比发现,只有引用类型受深拷贝浅拷贝的影响,值类型都是深拷贝!!

到这大概对深克隆,浅克隆有个大概映像了吧,那我们说正题,原型模式。

2、原型模式

2.1 基本介绍

具体可分为2个角色:

  Prototype(原型类):声明一个Clone(克隆)自身的接口;

  ConcretePrototype(具体原型类):,实现一个Clone(克隆)自身的操作。

在原型模式中,Prototype通常提供一个包含Clone方法的接口,具体的原型ConcretePrototype使用Clone方法完成对象的创建。

本质:通过拷贝这些原型对象创建新的对象。

根据其本质可以理解,原型本身就是通过一个自身的Clone方法来进行自我复制,从而产生新的对象。

既然是自我拷贝,那也就分为深拷贝与浅拷贝(具体参考上面)。

浅拷贝通过this.MemberWiseClone(),对实例的值类型进行拷贝(包含string类型),对引用类型只拷贝了引用。浅拷贝只对值类型成员进行复制,对于引用类型,只是复制了其引用,并不复制其对象。

深拷贝需要通过反射和序列化来实现。

 2.2 应用场景

对象在创建(new)时,消耗资源过多繁琐耗时。本质就是在对象的构造函数中有耗时长或者占用系统资源多的情况,使用原型模式进行复制对象时,可以省去这些耗时耗力的操作,直接获得对象的具体实例。

最常见的使用场景之一就是对象历史节点的保存,比如在对对象进行操作一次后,进行一次复制保存当前状态(恢复到某一历史状态),可实现撤销操作。

2.3 具体实例

原型类

    /// <summary>/// 原型类/// </summary>[Serializable]//表示此类可以序列化,深克隆必须声明该特性public abstract class MyClass{/// <summary>/// 值类型/// </summary>public int age { get; set; }/// <summary>/// 引用类型/// </summary>public Other other { get; set; }/// <summary>/// 拷贝方法/// </summary>/// <returns></returns>public abstract MyClass Clone();}

 具体原型类(浅拷贝)

    /// <summary>/// 创建具体原型/// </summary>public class My : MyClass{/// <summary>/// 浅克隆/// </summary>/// <returns></returns>public override MyClass Clone(){return (MyClass)base.MemberwiseClone();}}

使用

private void WTBtn_Click(object sender, EventArgs e)
{//实例化一个引用类型My my = new My();my.other = new Other();my.other.Id = 21;my.age = 12;Console.WriteLine($"拷贝前:other.Id:{my.other.Id},age{my.age}");var AA = my.Clone();//深拷贝。AA.age = 15;AA.other.Id = 51;Console.WriteLine($"拷贝后:other.Id:{my.other.Id},age{my.age}");
}

具体原型类(深拷贝) 

 [Serializable]public class My : MyClass{/// <summary>/// 深克隆/// </summary>/// <returns></returns>public override MyClass Clone(){using (var memoryStream = new MemoryStream()){var formatter = new BinaryFormatter();formatter.Serialize(memoryStream, this);memoryStream.Position = 0;return (MyClass)formatter.Deserialize(memoryStream);}}}

使用

private void WTBtn_Click(object sender, EventArgs e)
{//实例化一个引用类型My my = new My();my.other = new Other();my.other.Id = 21;my.age = 12;Console.WriteLine($"拷贝前:other.Id:{my.other.Id},age{my.age}");var AA = my.Clone();//深拷贝。AA.age = 15;AA.other.Id = 51;Console.WriteLine($"拷贝后:other.Id:{my.other.Id},age{my.age}");
}

结语:知道什么是深克隆什么是浅克隆后,原型模式理解起来就不会有太大的困难了。

END................................................................................................................................................................ 

相关文章:

C# 创建型设计模式----原型模式

1、值类型与引用类型、深拷贝与浅拷贝。 在了解原型模式前得先对这四个知识点有些了解。我先简单介绍一下这四个知识点。 1.1 值类型与引用类型(C#仅有这两种数据类型) 值类型: 常见的值类型&#xff1a;int、long、short、byte、float、double、bool、char、Struct&#xf…...

Python数据分析NumPy和pandas(十五、pandas 数据加载、存储和文件格式)

大多数时候&#xff0c;我们要处理分析的数据是存储在不同格式的文件中的&#xff0c;有txt、csv、excel、json、xml以及二进制等磁盘文件格式&#xff0c;还有时候是从数据库以及从Web API中交互获取要处理的数据。现在开始学习如何用pandas从以上内容中输入和输出数据。 读取…...

正则表达式以及密码匹配案例手机号码脱敏案例

目录 正则表达式 什么是正则表达式 语法 定义变量 test方法 exec方法 replace方法 match方法 修饰符 元字符 边界符 单词边界 字符串边界 边界符&#xff1a;^ 边界符&#xff1a;$ 量词 * ? {n} {n,} {n,m} 字符类 []匹配字符集合 .匹配除换行符之外的…...

五、数组切片make

数组&切片&make 1. 数组2. 多维数组3. 切片3.1 直接声明新的切片函数构造切片3.3 思考题3.4 切片和数组的异同 4. 切片的复制5. map5.1 遍历map5.2 删除5.3 线程安全的map 6. nil7. new和make 1. 数组 数组是一个由固定长度的特定类型元素组成的序列&#xff0c;一个数…...

SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测

SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测 目录 SSA-CNN-LSTM-MATT多头注意力机制多特征分类预测分类效果基本介绍程序设计参考资料 分类效果 基本介绍 1.Matlab实现SSA-CNN-LSTM-MATT麻雀算法优化卷积神经网络-长短期记忆神经网络融合多头注意力机制多特征分类预测&…...

51单片机完全学习——LCD1602液晶显示屏

一、数据手册解读 通过看数据手册我们需要知道&#xff0c;这个屏幕每个引脚的定义以及如何进行发送和接收。通过下面这张图我们就可以知道&#xff0c;这些引脚和我们的编程是有关的&#xff0c;需要注意的是&#xff0c;这里我们在接线的时候&#xff0c;一定要把DB0-DB7接到…...

【知识科普】今天聊聊前端打包工具webpack

文章目录 webpack概述1. 入口&#xff08;Entry&#xff09;2. 输出&#xff08;Output&#xff09;3. Loader4. 插件&#xff08;Plugins&#xff09;5. 模式&#xff08;Mode&#xff09;6. 浏览器兼容性&#xff08;Browser Compatibility&#xff09;7. 环境&#xff08;En…...

雷池社区版中升级雷池遇到问题

关于升级后兼容问题 版本差距过大会可能会发生升级后数据不兼容导致服务器无法起来 跨多个版本&#xff08;超过1个大版本号&#xff09;升级做好数据备份&#xff0c;遇到升级失败可尝试重新安装解决 升级提示目录不对 在错误的目录下执行&#xff08;比如 safeline 的子目…...

C++基础:constexpr,类型转换和选择语句

constexpr 提到constexpr&#xff0c;我们会发现它和const类比 常和const类比constexpr符号常量必须给定一个在编译时已知的值&#xff0c; 若某个变量初始化时的值在编译时未知&#xff0c;但初始化后绝不变。 #include<iostream> #include<vector> #include&l…...

STM32 RTC时间无法设置和读取

hal_stm32_RTC函数_stm32 hal rtc-CSDN博客 STM32入门HAL库-RTC实时时钟_hal rtc-CSDN博客 参考了这些博客&#xff0c;是调试发现无法读取正确的时间&#xff0c;日期可以 通过读hal库的文件找到原因 --RTC_BINARY_ONLY模式&#xff0c;只有 sTime->SubSeconds only is …...

go语言中defer用法详解

defer 是 Go 语言中的一个关键字&#xff0c;用于延迟执行某个函数或语句&#xff0c;直到包含它的函数返回时才执行。defer 语句在函数执行结束后&#xff08;无论是正常返回还是由于 panic 返回&#xff09;都将执行。 defer 的基本用法 延迟执行&#xff1a; 当你在一个函数…...

iOS 18.2开发者预览版 Beta 1版本发布,欧盟允许卸载应用商店

苹果今天为开发人员推送了iOS 18.2开发者预览版 Beta 1版本 更新&#xff08;内部版本号&#xff1a;22C5109p&#xff09;&#xff0c;本次更新距离上次发布 Beta / RC 间隔 2 天。该版本仅适用于支持Apple Intelligence的设备&#xff0c;包括iPhone 15 Pro系列和iPhone 16系…...

【SQL】SQL函数

&#x1f4e2; 前言 函数 是指一段可以直接被另一段程序调用的程序或代码。主要包括了以下4中类型的函数。 字符串函数数值函数日期函数流程函数 &#x1f384; 字符串函数 ⭐ 常用函数 函数 功能 CONCAT(S1,S2,...Sn) 字符串拼接&#xff0c;将S1&#xff0c;S2&#xff0…...

NSSCTF刷题篇web部分

源码泄露 [FSCTF 2023]寻找蛛丝马迹 这个源码泄露&#xff0c;可以记录一下&#xff0c;涉及的知识点比较多 打开环境 查看源码&#xff0c; 第一段flag 乱码&#xff0c;恢复一下 乱码恢复网站&#xff1a;乱码恢复 (mytju.com) 剩下的就只说方法 http://node4.anna.nss…...

超子物联网HAL库笔记:准备篇

超子物联网 HAL库学习 汇总入口&#xff1a; 超子物联网HAL库笔记&#xff1a;[汇总] 写作不易&#xff0c;如果您觉得写的不错&#xff0c;欢迎给博主来一波点赞、收藏~让博主更有动力吧&#xff01; 1. HAL库简介 HAL库 HAL库&#xff08;Hardware Abstraction Layer&#…...

FoRAG:面向网络增强型长文本问答的事实优化检索增强生成方法

人工智能咨询培训老师叶梓 转载标明出处 检索增强生成技术尽管出现了各种开源方法和商业系统&#xff0c;如Bing Chat&#xff0c;但生成的长文本答案中缺乏事实性和清晰逻辑的问题仍未得到解决。为了解决这些问题&#xff0c;来自蚂蚁集团和清华大学的研究者们提出了一种名为…...

Android NSD局域网发现服务

近期在了解局域网发现服务的时候无意间看到Android 自带的&#xff08;Network Service Discovery&#xff09;网络发现服务&#xff0c;在一番验证之后发现实现比较简单&#xff0c;可靠性也高&#xff0c;因此在这里做一个整理&#xff0c;算是对自己知识做一个归档。 网络服…...

算法的学习笔记—左旋转字符串(牛客JZ58)

&#x1f600;前言 在程序设计中&#xff0c;字符串处理问题屡见不鲜&#xff0c;其中“字符串左旋”是一种常见操作&#xff0c;今天我们一起来探讨一个经典的左旋转字符串题目&#xff0c;以及一种优雅的解决方案——三步翻转法。 &#x1f3e0;个人主页&#xff1a;尘觉主页…...

Mac 上无法烧录 ESP32C3 的问题记录:A fatal error occurred:Failed to write to target RAM

文章目录 问题描述驱动下载地址问题解决&#xff1a;安装 CH343 驱动踩的坑日志是乱码 问题描述 我代码编译可以&#xff0c;但是就是烧录不上去 A fatal error occurred:Failed to write to target RAM(result was 01070000:Operation timed out) Uploaderror:上传失败&…...

ios 项目升级极光SDK

由于项目使用的是旧版本&#xff0c;隐私合规检查不通过&#xff0c;需要升级到最新版本&#xff0c; 使用cocoapods集成无法正常运行&#xff0c;.a文件找不到&#xff0c;可能项目比较久了&#xff0c;最好选择手动导入 下载最新版本SDK&#xff0c;将 SDK 包解压&#xff…...

【Java】java | logback日志配置 | 按包配置级别

一、概述 日志配置需求&#xff1a; 本地部分包开debug&#xff0c;其他路径走配置&#xff1b;只在本地环境有效 二、logback.xml配置 <!--本地调试&#xff0c;开debug--> <springProfile name"dev"><logger name"cn.hg.demo" level&quo…...

Virtuoso使用layout绘制版图、使用Calibre验证DRC和LVS

1 绘制版图 1.1 进入Layout XL 绘制好Schmatic后&#xff0c;在原理图界面点击Launch&#xff0c;点击Layout XL进入版图绘制界面。 1.2 导入元件 1、在Layout XL界面左下角找打Generate All from Source。 2、在Generate Layout界面&#xff0c;选中“Instance”&#…...

Spring框架原理面试题及参考答案

目录 什么是Spring 开发框架? 说说Spring 的 IOC 和 DI? 简述IoC(控制反转)及在 Spring 中的实现 说说Spring IOC 容器的基本概念? 说说Spring IoC 的实现机制? 说说Spring IoC 容器? 简述Spring ApplicationContext 说说Spring Bean 的生命周期 说说在 Spring…...

Java类的static成员以及代码块(详细版)

文章目录 一、什么是static成员二、static修饰的成员有何意义三、static修饰成员变量四、static修饰成员方法4.1、静态成员变量不可以在方法内创建4.2、静态成员方法内部不可以访问非静态成员变量4.3、总结 五、static成员变量的初始化5.1、就地初始化5.2、静态代码块初始化 六…...

Opensearch集群部署【docker、服务器、Helm多种部署方式】

操作系统兼容性 我们建议在 Red Hat Enterprise Linux (RHEL) 或使用systemd的基于 Debian 的 Linux 发行版上安装 OpenSearch &#xff0c;例如 CentOS、Amazon Linux 2 和 Ubuntu Long-Term Support (LTS)。OpenSearch 应该适用于大多数 Linux 发行版&#xff0c;但我们只测…...

本地Docker部署开源WAF雷池并实现异地远程登录管理界面

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

性能需求笔记

名称解释 系统用户&#xff1a;所有注册过的用户&#xff1b;在线用户&#xff1a;某时间段内登录且在线的用户 pv&#xff1a;用户浏览页面的次数 UV&#xff1a;登录系统的用户&#xff0c;uv课产生多个pv 性能测试&#xff1a;测试软件在系统中的运行性能&#xff0c;度量系…...

ts:数组的常用方法(reduce累加)

ts&#xff1a;数组的常用方法&#xff08;reduce累加&#xff09; 一、主要内容说明二、例子reduce方法&#xff08;累加&#xff09;1.源码1 &#xff08;reduce方法&#xff09;2.源码1运行效果 三、结语四、定位日期 一、主要内容说明 ts中数组的reduce方法&#xff0c;用…...

Begin

cpp 编程的发展方向还是很多的&#xff1a;游戏、嵌入式、QT、客户端、服务端、机器学习、算法大模 型 ...&#xff0c;现阶段还是不太清楚具体想走什么方向。主QT编程应该是不在考虑之内的&#xff0c;可以为辅简单 学习一下&#xff1b;游戏方向&#xff1a;需要学习lua语言…...

【实战案例】Django框架表单处理及数据库交互

本文基于之前内容列表如下&#xff1a; 【图文指引】5分钟搭建Django轻量级框架服务 【实战案例】Django框架基础之上编写第一个Django应用之基本请求和响应 【实战案例】Django框架连接并操作数据库MySQL相关API 【实战案例】Django框架使用模板渲染视图页面及异常处理 更新编…...