设计模式-原型模式
c#内存分配:https://www.cnblogs.com/Kyle-Wang/p/16102650.html
一、原型模式引入
很多时候,我们需要大量相同或者类似的对象,比如做五子棋的棋盘,每个格子都是类似的对象,只是坐标不一样而已。如果使用new对象的方法,在性能上肯定会有损耗。而原型模式就是解决这个问题。
定义:原型模式就是用利用对象copy,快速获取对象
二、创建原型类
/// <summary> /// 原型类 /// </summary> public class StudentPrototype { public int Id { get; set; } public string Name { get; set; } public Class Class { get; set; } /// <summary> /// 1 构造函数私有化--避免随意构造 /// </summary> public StudentPrototype() { Thread.Sleep(2000); long lResult = 0; for (int i = 0; i < 1000000; i++) { lResult += i; } Console.WriteLine("{0}被构造..", this.GetType().Name); } public void Study() { Console.WriteLine("{0}在学习{1}", this.Name,this.Class.ClassName); } }
该原型类的构造函数每new一次都很费时,所以需要拷贝。
对象的拷贝分为浅拷贝和深拷贝。涉及到一些内存分配的知识,请参考"c#内存分配"
三、浅拷贝实现原型模式
1、浅拷贝就是当复制一个对象时,如果这个对象包含有引用类型的字段,那所有复制出来的对象里的引用类型的字段是指向原对象的该字段引用。简单讲就是:浅拷贝只复制了值类型的值,没有复制新一份的引用类型的值。
2、实现方式
/// <summary> /// 原型类 /// </summary> public class StudentPrototype { public int Id { get; set; } public string Name { get; set; } public Class Class { get; set; } /// <summary> /// 1 构造函数私有化--避免随意构造 /// </summary> private StudentPrototype() { Thread.Sleep(2000); long lResult = 0; for (int i = 0; i < 1000000; i++) { lResult += i; } Console.WriteLine("{0}被构造..", this.GetType().Name); } /// <summary> /// 3 私有的静态字段--内存唯一,不会释放,且在第一次使用这个类被初始化且只初始化一次 /// </summary> private static StudentPrototype _Student = new StudentPrototype() { Id = 123, Name = "ywa", Class = new Class() { ClassId = 1, ClassName = ".Net高级班" } }; /// <summary> /// 2 公开的静态方法来提供实例 /// </summary> /// <returns></returns> public static StudentPrototype CreateInstance() { StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone(); //MemberwiseClone:内存拷贝,不走构造函数,直接内存copy,所以没有性能损失;而且产生的是新对象----浅拷贝--只拷贝引用 return student; } public void Study() { Console.WriteLine("{0}在学习{1}", this.Name,this.Class.ClassName); } } public class Class { public int ClassId { get; set; } public string ClassName { get; set; } }
使用c#自带的MemberwiseClone来进行浅拷贝
3、测试
{ Console.WriteLine("***************Prototype**************"); StudentPrototype student1 = StudentPrototype.CreateInstance(); StudentPrototype student2 = StudentPrototype.CreateInstance(); StudentPrototype student3 = StudentPrototype.CreateInstance(); student1.Id = 234; student1.Name = "CodeMan"; student1.Class.ClassId = 2; student1.Class.ClassName = "架构班"; //因为string类型的 ="CodeMan" 等同于 new String("CodeMan"); 开辟新的空间,不影响之前的-----实际上string是不可修改的---- student2.Id = 345; student2.Name = "左耳听风"; student2.Class.ClassId = 3; student2.Class.ClassName = "全栈班"; student3.Id = 456; student3.Name = "NULL"; student3.Class = new Class() { ClassId = 4, ClassName = "实战班" };//换了个引用 student1.Study(); student2.Study(); student3.Study(); //student1的 classid student2 student3 234 444 或者其他 //C#内存分配----334 }
Student.Name被重新赋值了,因为string a = "abc";类似于String a = new String("abc");所以Student.Name都指向不同的引用了,但是Student1.Class和Student2.Class由于是引用类型,且没有重新赋值新引用,所以指向同一个内存块。Student3.Class有重新赋值引用,这里相当于深拷贝了。
四、深拷贝实现原型模式
1、深拷贝就是当复制一个对象时,完完全全的赋值出一个新对象出来,不同原对象里是否有引用类型的字段。
2、实现方式
一种方式就是上面student3那样,对引用类型的字段重新new一下,但这样比较麻烦,要是Class对象里面又有对象,很麻烦。
这里采用序列化和反序列化的方式实现深拷贝
/// <summary> /// 原型类 /// </summary> [Serializable] public class StudentPrototype { /// <summary> /// 1 构造函数私有化--避免随意构造 /// </summary> private StudentPrototype() { Thread.Sleep(2000); long lResult = 0; for (int i = 0; i < 1000000; i++) { lResult += i; } Console.WriteLine("{0}被构造..", this.GetType().Name); } /// <summary> /// 3 私有的静态字段--内存唯一,不会释放,且在第一次使用这个类被初始化且只初始化一次 /// </summary> private static StudentPrototype _Student = new StudentPrototype() { Id = 123, Name = "ywa", Class = new Class() { ClassId = 1, ClassName = ".Net高级班" } }; /// <summary> /// 2 公开的静态方法来提供实例 /// </summary> /// <returns></returns> public static StudentPrototype CreateInstance() { StudentPrototype student = (StudentPrototype)_Student.MemberwiseClone(); return student; } //deepcopy:1 直接new 2 子类型提供原型方式 3 序列化反序列化 public static StudentPrototype CreateInstanceSerialize() { return SerializeHelper.DeepClone<StudentPrototype>(_Student); } public int Id { get; set; } public string Name { get; set; } public Class Class { get; set; } public void Study() { Console.WriteLine("{0}在学习{1}", this.Name,this.Class.ClassName); } } [Serializable] public class Class { public int ClassId { get; set; } public string ClassName { get; set; } }
public class SerializeHelper { public static string Serializable(object target) { using (MemoryStream stream = new MemoryStream()) { new BinaryFormatter().Serialize(stream, target); return Convert.ToBase64String(stream.ToArray()); } } public static T Derializable<T>(string target) { byte[] targetArray = Convert.FromBase64String(target); using (MemoryStream stream = new MemoryStream(targetArray)) { return (T)(new BinaryFormatter().Deserialize(stream)); } } public static T DeepClone<T>(T t) { return Derializable<T>(Serializable(t)); } }
需要深拷贝的类必须加上【Serializable】特性,然后把原型对象序列化为二进制字符串,再反序列化成一个新对象。这个新对象就是完全在内存空间新开辟的一块内存块。