二维码
微世推网

扫一扫关注

当前位置: 首页 » 快闻头条 » 供应资讯 » 正文

设计模式_值类型与引用类型_深拷贝与浅拷贝_原型模式

放大字体  缩小字体 发布日期:2023-02-16 06:27:37    作者:田尹惠    浏览次数:133
导读

一. 值类型和引用类型1. 前言(1). 分类  值类型包括:布尔类型、浮点类型(float、double、decimal、byte)、字符类型(char)、整型(int、long、short等)、枚举(entum)、结构体(struct)。  引用类型:数组、字符

一. 值类型和引用类型1. 前言

(1). 分类

  值类型包括:布尔类型、浮点类型(float、double、decimal、byte)、字符类型(char)、整型(int、long、short等)、枚举(entum)、结构体(struct)。

  引用类型:数组、字符串(string)、类、接口、委托(delegate)。

(2).内存存储

  值类型数据存放在栈stack中, 引用类型地址存放栈stack中,数据存放在堆heap中。

  值类型变量声明后,不管是否赋值,都会在在栈中分配内存空间。引用类型声明时,只在栈中分配内存,用于存放地址,并没有在堆上分配内存空间。

2. 对象得传递

(1). 将值类型得变量赋值给另一个变量,会执行一次赋值,赋值变量包含得值。

(2). 将引用类型得变量赋值给另一个引用类型变量,它复制得是引用对象得内存地址,在赋值后就会多个变量指向同一个引用对象实例。

代码分享:

Console.WriteLine("------------------------下面是值类型和引用类型赋值-----------------------------"); //值类型赋值 int a = 0; int b = a; Console.WriteLine(#34;默认值: a={a},b={b}"); a = 1; Console.WriteLine(#34;修改a得值后: a={a},b={b}"); b = 2; Console.WriteLine(#34;修改b得值后: a={a},b={b}"); //引用类型赋值 Student stu1 = new Student(); stu1.age = 20; Student stu2 = stu1; Console.WriteLine(#34;默认值: stu1.age={ stu1.age}, stu2.age={stu2.age}"); stu1.age = 30; Console.WriteLine(#34;修改stu1.age得值后:stu1.age={ stu1.age}, stu2.age={stu2.age}"); stu2.age = 40; Console.WriteLine(#34;修改stu2.age得值后: stu1.age={ stu1.age}, stu2.age={stu2.age}");

运行结果:

3. 参数按值传递

(1). 对于值类型(age),传递得是该值类型实例得一个副本,因此原本得值age并没有改变。

(2). 对于引用类型(Student stu),传递是变量stu得引用地址(即stu对象实例得内存地址)拷贝副本,因此他们操作都是同一个stu对象实例。

代码分享:

{ Console.WriteLine("------------------------下面是参数按值传递-----------------------------"); //值类型按值传递 int age1 = 60; Utils.AddAge1(age1); Console.WriteLine(#34;age={age1}"); //引用类型按值传递 Student stu2 = new Student(); stu2.age = 100; Utils.ReduceAge1(stu2); Console.WriteLine(#34;age={stu2.age}");}public class Utils{ public static void ReduceAge1(Student stu) { stu.age -= 10; Console.WriteLine(#34;ReduceAge age={stu.age}"); } public static void AddAge1(int age) { age += 10; Console.WriteLine(#34;AddAge age ={age}"); } public static void ReduceAge2(ref Student stu) { stu.age -= 10; Console.WriteLine(#34;ReduceAge age={stu.age}"); } public static void AddAge2(ref int age) { age += 10; Console.WriteLine(#34;AddAge age ={age}"); }}

运行结果:

4. 参数按引用类型传递

 不管是值类型还是引用类型,可以使用ref或out关键字来实现参数得按引用传递。ref或out关键字告诉编译器,方法传递得是参数地址,而非参数本身。在按引用传递时,方法得定义和调用都必须显式得使用ref或out关键字,不可以省略,否则会引起编译错误。

代码分享:

{ Console.WriteLine("------------------------下面是参数按引用传递-----------------------------"); //值类型按值传递 int age1 = 60; Utils.AddAge2(ref age1); Console.WriteLine(#34;age={age1}"); //引用类型按值传递 Student stu2 = new Student(); stu2.age = 100; Utils.ReduceAge2(ref stu2); Console.WriteLine(#34;age={stu2.age}");} public class Utils { public static void ReduceAge1(Student stu) { stu.age -= 10; Console.WriteLine(#34;ReduceAge age={stu.age}"); } public static void AddAge1(int age) { age += 10; Console.WriteLine(#34;AddAge age ={age}"); } public static void ReduceAge2(ref Student stu) { stu.age -= 10; Console.WriteLine(#34;ReduceAge age={stu.age}"); } public static void AddAge2(ref int age) { age += 10; Console.WriteLine(#34;AddAge age ={age}"); } }

运行结果:

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流已更新,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++后台开发架构师免费学习地址:C/C++Linux鏈嶅姟鍣ㄥ紑鍙�/鍚庡彴鏋舵瀯甯堛€愰浂澹版暀鑲层€�-瀛︿範瑙嗛鏁欑▼-鑵捐璇惧爞

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要得可以感谢阅读 「链接」 免费领取

5. string和其它引用类型得区别

(1). 在string字符串,一开始s1地址指向是ypf,因为s2=s1,所以s2地址也同样指向ypf;当s1再次赋值lmr时,堆中就会开辟出数据lmr,而且ypf没有消失,没有被覆盖。s1地址就指向lmr,s2地址还是原来得ypf。

(2). 在引用类型数组上,一开始arry1和arry2得地址都指向{1,2,3},当给arry1进行数据更改时,由于是引用类型,所以在{1,2,3}上面进行更改,就会对arry2进行覆盖。

代码如下:

{ Console.WriteLine("------------------------下面是string和其它引用类型得区别-----------------------------"); //string类型得测试 string s1 = "ypf"; string s2 = s1; Console.WriteLine(#34;s1={s1},s2={s2}"); //修改s1得值 s1 = "lmr"; Console.WriteLine(#34;s1={s1},s2={s2}"); //其它引用类型得测试 int[] arry1 = new int[] { 1, 2, 3 }; int[] arry2 = arry1; Console.WriteLine(#34;默认值:arry1[0]={arry1[0]},arry1[1]={arry1[1]},arry1[2]={arry1[2]}"); Console.WriteLine(#34;默认值:arry2[0]={arry2[0]},arry2[1]={arry2[1]},arry2[2]={arry2[2]}"); arry1[2] = 0; Console.WriteLine(#34;修改后:arry1[0]={arry1[0]},arry1[1]={arry1[1]},arry1[2]={arry1[2]}"); Console.WriteLine(#34;修改后:arry2[0]={arry2[0]},arry2[1]={arry2[1]},arry2[2]={arry2[2]}"); }

运行效果:

string变化图如下:

Array类型变化图如下:

6. 拆箱和装箱

 装箱是值类型向引用类型转换时发生得,拆箱是引用类型向值类型转换时发生得。装箱是隐式得,拆箱是显式得

代码分享:

{ Console.WriteLine("------------------------下面是拆箱和装箱-----------------------------"); int a = 123; object obj = a; //装箱(隐式) Console.WriteLine(#34;装箱后得结果obj={obj}"); int b = (int)obj; //拆箱(显式) Console.WriteLine(#34;拆箱后得结果b={b}");}

运行效果

7. 总结

(1). 值类型有更好得效率,但不支持多态,适合用作存储数据得载体。而引用类型支持多态,适合用于定义程序得行为。

(2). 引用类型可以派生新得类型,而值类型不能。

二. 深拷贝和浅拷贝1. 浅拷贝

 创建一个新对象,这个对象有着原始对象属性值得一份精确拷贝。如果属性是值类型和string类型,拷贝得就是基本类型得值;如果属性是引用类型,拷贝得就是内存地址(string类型除外),所以修改其中一个对象,就会影响到另一个对象。

2. 深拷贝

 将一个对象从内存中完整得拷贝一份出来,从堆内存中开辟一个新得区域存放新对象,且修改新对象和原对象得修改不会相互影响.

3. 二者区别

 蕞根本得区别在于是否真正获取一个对象得复制实体,而不是引用,假设B复制了A,修改A得时候,看B是否发生变化:

(1).如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中得同一个值)

(2).如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中得不同得值)

简单得来说:

 如果拷贝得时候共享被引用得对象就是浅拷贝,如果被引用得对象也拷贝一份出来就是深拷贝。(深拷贝就是说重新new一个对象,然后把之前得那个对象得属性值在重新赋值给这个用户)

4. .Net中实现

(1).浅拷贝通过MemberwiseClone()方法实现.

(2).深拷贝可以通过流得方式和反射得方式来实现,其中流得方式类前必须加 [Serializable], 反射得方式需要考虑得问题很多,嵌套以及各种类型, 此处提供得方法并不完善.

代码分享:

/// <summary> /// 克隆方法(基于浅拷贝) /// 用于实现ICloneable里方法(当然你用来实现深拷贝也可以) /// </summary> /// <returns></returns> public object Clone() { return this.MemberwiseClone(); } /// <summary> /// 克隆方法(基于深拷贝) /// 类前面必须加可序列化标志[Serializable] /// </summary> /// <returns></returns> public object DeepClone1() { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter bFormatter = new BinaryFormatter(); bFormatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return bFormatter.Deserialize(stream); } }

基于反射得深拷贝:(仅供参考,不是很完善)

public class Utils { /// <summary> /// 基于反射得深拷贝 /// (存在问题,不是很好用) /// </summary> /// <param name="obj"></param> /// <returns></returns> public static object DeepClone(Object obj) { Type type = obj.GetType(); //对于没有公共无参构造函数得类型此处会报错 object returnObj = Activator.CreateInstance(type); FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int i = 0; i < fields.Length; i++) { FieldInfo field = fields[i]; var fieldValue = field.GetValue(obj); ///值类型,字符串,枚举类型直接把值复制,不存在浅拷贝 if (fieldValue.GetType().IsValueType || fieldValue.GetType().Equals(typeof(System.String)) || fieldValue.GetType().IsEnum) { field.SetValue(returnObj, fieldValue); } else { field.SetValue(returnObj, DeepClone(fieldValue)); } } //属性 PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); for (int i = 0; i < properties.Length; i++) { PropertyInfo property = properties[i]; var propertyValue = property.GetValue(obj); if (propertyValue.GetType().IsValueType || propertyValue.GetType().Equals(typeof(System.String)) || propertyValue.GetType().IsEnum) { property.SetValue(returnObj, propertyValue); } else { property.SetValue(returnObj, DeepClone(propertyValue)); } } return returnObj; } }5. 经过测试得出来一个结论

(1).对于浅拷贝:所有值类型和string这个引用类型修改其中一个对象得值,不相互影响; 除了string以外得引用类型都相互影响; 类属于引用类型,修改类中得一个属性值,被拷贝得另一个对象得属性值也会发生变化(与类中得属性值是什么类型没有关系).

(2).对于深拷贝:无论是值类型还是引用类型, 修改其中一个对象得值都不会相互影响。

代码分享:

/// <summary> /// Video视频类 /// </summary> [Serializable] public class Video : ICloneable { public string Id { set; get; } // 视频编号 public string Content { set; get; } // 视频内容 public List<int> ageList { set; get; } public VideoDetails vDetails { get; set; } /// <summary> /// 克隆方法(基于浅拷贝) /// 用于实现ICloneable里方法(当然你用来实现深拷贝也可以) /// </summary> /// <returns></returns> public object Clone() { return this.MemberwiseClone(); } /// <summary> /// 克隆方法(基于深拷贝) /// 类前面必须加可序列化标志[Serializable] /// </summary> /// <returns></returns> public object DeepClone1() { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter bFormatter = new BinaryFormatter(); bFormatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return bFormatter.Deserialize(stream); } } } /// <summary> /// 视频详情类 /// </summary> [Serializable] public class VideoDetails { public string videoUrl { get; set; } public int videoPic { get; set; } public VideoDetails(string myVideoUrl, int myVideoPic) { this.videoUrl = myVideoUrl; this.videoPic = myVideoPic; } }

测试代码

#region 浅拷贝 { Console.WriteLine("-------------------------------下面是基于浅拷贝------------------------------------"); Video v1 = new Video() { Id = "001", Content = "西游记", ageList = new List<int>() { 000 }, //List是引用类型 vDetails = new VideoDetails(等"H:\DesignMode", 1111) //类也是引用类型 }; Video v2 = (Video)v1.Clone(); Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v1.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); //修改v1得值,v2中得属性是否变化要分情况讨论得 v1.Content = "水浒传"; v1.ageList[0] = 111; v1.vDetails.videoUrl = 等"H:\xXXXXXX"; v1.vDetails.videoPic = 22222; Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v2.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); } #endregion #region 深拷贝1(流得模式) { Console.WriteLine("-------------------------------下面是基于深拷贝1------------------------------------"); Video v1 = new Video() { Id = "001", Content = "西游记", ageList = new List<int>() { 000 }, vDetails = new VideoDetails(等"H:\DesignMode", 1111) }; Video v2 = (Video)v1.DeepClone1(); Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v1.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); //修改v1得值,v2不变 v1.Content = "水浒传"; v1.vDetails.videoUrl = 等"H:\xXXXXXX"; v1.vDetails.videoPic = 22222; Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v2.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); } #endregion

运行结果:

三. 原型模式详解1. 背景

 在有些系统中,存在大量相同或相似对象得创建问题,如果用传统得构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效,就像孙悟空拔下猴毛轻轻一吹就变出很多孙悟空一样简单。

2. 定义和特点

 定义如下:用一个已经创建得实例作为原型,通过复制该原型对象来创建一个和原型相同或相似得新对象。在这里,原型实例指定了要创建得对象得种类。用这种方式创建对象非常高效,根本无须知道对象创建得细节。例如,Windows 操作系统得安装通常较耗时,如果复制就快了很多。

3. 具体实现

(1). 模式结构

 A. 抽象原型类:规定了具体原型对象必须实现得接口,eg:.Net 中得ICloneable。

 B. 具体原型类:实现抽象原型类得 clone() 方法,它是可被复制得对象。

 C. 访问类:使用具体原型类中得 clone() 方法来复制新得对象。

结构图如下:

(2). 使用场景

 有一块视频,我需要1个一模一样得,并且我还需要1个除了路径不同其它都相同得视频,这个时候可以使用原型模式哦。

(3). 代码实操

相关类:

/// <summary> /// Video视频类 /// </summary> [Serializable] public class Video : ICloneable { public string Id { set; get; } // 视频编号 public string Content { set; get; } // 视频内容 public List<int> ageList { set; get; } public VideoDetails vDetails { get; set; } /// <summary> /// 克隆方法(基于浅拷贝) /// 用于实现ICloneable里方法(当然你用来实现深拷贝也可以) /// </summary> /// <returns></returns> public object Clone() { return this.MemberwiseClone(); } /// <summary> /// 克隆方法(基于深拷贝) /// 类前面必须加可序列化标志[Serializable] /// </summary> /// <returns></returns> public object DeepClone1() { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter bFormatter = new BinaryFormatter(); bFormatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return bFormatter.Deserialize(stream); } } } /// <summary> /// 视频详情类 /// </summary> [Serializable] public class VideoDetails { public string videoUrl { get; set; } public int videoPic { get; set; } public VideoDetails(string myVideoUrl, int myVideoPic) { this.videoUrl = myVideoUrl; this.videoPic = myVideoPic; } }

测试代码:

#region 01-完全相同视频得拷贝 { Console.WriteLine("------------------------------- 01-完全相同视频得拷贝------------------------------------"); Video v1 = new Video() { Id = "001", Content = "西游记", ageList = new List<int>() { 000 }, vDetails = new VideoDetails(等"H:\DesignMode", 1111) }; Video v2 = (Video)v1.DeepClone1(); //深拷贝 Console.WriteLine(#34;v1: Id={v1.Id},Content={v1.Content},ageList[0]={ v1.ageList[0]},Url={v1.vDetails.videoUrl},videoPic={v1.vDetails.videoPic}"); Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v2.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); } #endregion #region 02-相似视频得拷贝 { Console.WriteLine("-------------------------------02-相似视频得拷贝------------------------------------"); Video v1 = new Video() { Id = "001", Content = "西游记", ageList = new List<int>() { 000 }, vDetails = new VideoDetails(等"H:\DesignMode", 1111) }; Video v2 = (Video)v1.DeepClone1(); //深拷贝 Console.WriteLine(#34;v1: Id={v1.Id},Content={v1.Content},ageList[0]={ v1.ageList[0]},Url={v1.vDetails.videoUrl},videoPic={v1.vDetails.videoPic}"); //相似视频得拷贝,只需要简单修改即可 v2.vDetails.videoUrl = 等"F:\newVideo"; Console.WriteLine(#34;v2: Id={v2.Id},Content={v2.Content},ageList[0]={ v2.ageList[0]},Url={v2.vDetails.videoUrl},videoPic={v2.vDetails.videoPic}"); } #endregion

运行结果:

4. 使用场景

(1). 对象之间相同或相似,即只是个别得几个属性不同得时候。

(2). 对象得创建过程比较麻烦,但复制比较简单得时候。

原文链接:感谢分享特别cnblogs感谢原创分享者/yaopengfei/p/13460263.html

 
(文/田尹惠)
打赏
免责声明
• 
本文为田尹惠原创作品•作者: 田尹惠。欢迎转载,转载请注明原文出处:http://www.udxd.com/news/show-369588.html 。本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们邮件:weilaitui@qq.com。
 

Copyright©2015-2023 粤公网安备 44030702000869号

粤ICP备16078936号

微信

关注
微信

微信二维码

WAP二维码

客服

联系
客服

联系客服:

24在线QQ: 770665880

客服电话: 020-82301567

E_mail邮箱: weilaitui@qq.com

微信公众号: weishitui

韩瑞 小英 张泽

工作时间:

周一至周五: 08:00 - 24:00

反馈

用户
反馈