原型模式就是用于创建重复的对象,当想要创建一个新的对象但是开销比较大或者想将对象的当前状态保存下来的时候,我们就可以使用原型模式。

创建原型

public abstract class Base
{
//因为String的特殊性,所以此次演示我们使用StringBuilder
public StringBuilder Name { get; set; }
public int Age { get; set; } public Base()
{
//模拟创建对象花费的开销
Thread.Sleep();
} public Base(String name, int age)
{
this.Name = new StringBuilder(name);
this.Age = age;
//模拟创建对象花费的开销
Thread.Sleep();
} //深拷贝
public abstract Base Clone();
//浅拷贝
public abstract Base MClone();
}

接下来创建一个Peron类,继承Base,并且实现两个复制方法

//如果是要通过序列化来进行深拷贝的话,要打上Serializable标签
[Serializable]
public class Person : Base
{
public Person()
: base()
{ }
public Person(String name, int age)
: base(name, age)
{ }
/// <summary>
/// 深拷贝
/// </summary>
/// <returns>返回一个全新的Person对象</returns>
public override Base Clone()
{
//创建一个内存流
MemoryStream ms = new MemoryStream();
//创建一个二进制序列化对象
BinaryFormatter bf = new BinaryFormatter();
//将当前对象序列化写入ms内存流中
bf.Serialize(ms, this);
//设置流读取的位置
ms.Position = ;
//将流反序列化为Object对象
return bf.Deserialize(ms) as Person;
} /// <summary>
/// 浅拷贝
/// </summary>
/// <returns></returns>
public override Base MClone() =>
//浅拷贝
this.MemberwiseClone() as Person;
}

Main方法中调用,首先我们每次都创建新的Person对象

static void Main(string[] args)
{
//用于计时
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); Person p = new Person("a",2);
Person p2 = new Person("a",);
Person p3 = new Person("a",2); stopwatch.Stop();
Console.WriteLine("耗时:"+stopwatch.Elapsed);
Console.ReadKey();
}

运行结果:

可见如果创建对象如果开销很大的话,每次用的时候都创建效率就会很低

接下来我们使用原型模式来创建重复的对象,调用MClone()浅拷贝

static void Main(string[] args)
{
//用于计时
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); Person p = new Person("a",);
Person p1 = p.MClone() as Person;
Person p2 = p.MClone() as Person; //记录下来name的值,后续通过即时窗口查看
StringBuilder name2 = p2.Name;
StringBuilder name1 = p1.Name; stopwatch.Stop();
Console.WriteLine("耗时:"+stopwatch.Elapsed);
Console.ReadKey();
}

在Console.ReadKey();处设置断点,运行程序,打开  调试>>窗口>>即时,在右下角即时窗口输入  *&name1  回车,*&name2   回车,查看name1和name2的内存地址

我们可以看到,name1和name2的内存地址都是相同的,说明p2.Name和p1.Name是指向了同一个引用。所以对于属性是引用类型的对象,实现浅拷贝,所有的属性引用只会指向同一个对象,也就是说只要有一个对象修改了Name属性,其他的对象的Name属性都会发生改变。

看一下运行结果

可以发现,只有第一次创建对象需要很大的开销,通过原型复制的话是很快的。

接下来我们看一下深拷贝

static void Main(string[] args)
{
//用于计时
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start(); Person p = new Person("a",);
Person p1 = p.Clone() as Person;
Person p2 = p.Clone() as Person; //记录下来name的值,后续通过即时窗口查看
StringBuilder name2 = p2.Name;
StringBuilder name1 = p1.Name; stopwatch.Stop();
Console.WriteLine("耗时:"+stopwatch.Elapsed);
Console.ReadKey();
}

执行和刚才相同的操作,来看一下内存地址

可以发现,p1和p2的Name在内存中的地址是不一样的, 就是说明深拷贝会将对象的所有非静态属性都复制一份,如果碰到引用类型也会重新创建一份,而不是复制指向对象的引用。

最后我们看一下运行结果

C#原型模式(深拷贝、浅拷贝)的更多相关文章

  1. Prototype 原型模式 复制 浅拷贝 clone MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  2. Java原型模式之浅拷贝-深拷贝

    一.是什么? 浅拷贝:对值类型的成员变量进行值的复制,对引用类型的成员变量仅仅复制引用,不复制引用的对象 深拷贝:对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制 内部机制: ...

  3. 设计模式:原型模式介绍 && 原型模式的深拷贝问题

    0.背景 克隆羊问题:有一个羊,是一个类,有对应的属性,要求创建完全一样的10只羊出来. 那么实现起来很简单,我们先写出羊的类: public class Sheep { private String ...

  4. Java原型模式之基础

    一.是什么? 定义:用原型实例指定创建对象的种类,而且通过拷贝这些原型创建新的对象.(官方定义) 原型模式主要用于对象的复制,它的核心是就是类图中的原型类Prototype. Prototype类须要 ...

  5. 设计模式学习-使用go实现原型模式

    原型模式 定义 代码实现 优点 缺点 适用场景 参考 原型模式 定义 如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复 ...

  6. C#设计模式:原型模式(Prototype)及深拷贝、浅拷贝

    原型模式(Prototype) 定义: 原型模式:用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象.被复制的实例被称为原型,这个原型是可定制的. Prototype Pattern也是一 ...

  7. 设计模式_11_原型模式(prototype)深拷贝、浅拷贝

    设计模式_11_原型模式(prototype) 浅拷贝: package designPatternOf23; /** * 定义:用原型实例,指定创建对象的种类,并通过拷贝这些原型创建新的对象 * P ...

  8. 设计模式之原型模式(深入理解OC中的NSCopying协议以及浅拷贝、深拷贝)

    原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.原型模式其实就是从一个对象再创建另一个可定制的对象,而且不需知道任何创建的细节. 比如说,有一个Person类,有firstN ...

  9. Java clone克隆方法 --深拷贝--浅拷贝 --原型模型

    什么是深拷贝? 什么是浅拷贝? 创建一个对象的方法有几种? 默认的Object方法中的clone是深拷贝还是浅拷贝? 为什么说很多深拷贝都是不彻底的深拷贝? 什么是原型模型,什么是原型模式? 原型模型 ...

随机推荐

  1. java中级,知识点归纳(一)

    一.接口和抽象类的区别 抽象类中可以含有构造方法,而接口内不能有. 抽象类中可以有普通成员变量,而接口中不能有. 抽象类中可以包含非抽象的普通方法,而接口中所有方法必须是抽象的,不能有非抽象的普通方法 ...

  2. colmap编译过程中出现,无法解析的外部符号错误 “__cdecl google::base::CheckOpMessageBuilder::ForVar1(void)”

    错误提示: >colmap.lib(matching.obj) : error LNK2019: 无法解析的外部符号 "__declspec(dllimport) public: cl ...

  3. C# 常用类库(字符串处理,汉字首字母拼音,注入攻击,缓存操作,Cookies操作,AES加密等)

    十年河东,十年河西,莫欺少年穷 学无止境,精益求精 记录下字符串类库,方便今后查阅 主要包含了字符串解决,去除HTML,SQL注入攻击检测,IP地址处理,Cookies操作,根据身份证获取性别.姓名. ...

  4. Vue.js 源码分析(七) 基础篇 侦听器 watch属性详解

    先来看看官网的介绍: 官网介绍的很好理解了,也就是监听一个数据的变化,当该数据变化时执行我们的watch方法,watch选项是一个对象,键为需要观察的数据名,值为一个表达式(函数),还可以是一个对象, ...

  5. wpf 窗体添加背景图片

    方法一:xaml中:<控件>   <控件.Background><ImageBrush ImageSource="/WpfApplication1;compon ...

  6. vue--购物车案例(小知识点总结)

    Html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  7. mysql的floor()报错注入方法详细分析

    刚开始学习sql注入,遇见了 select count(*) from table group by floor(rand(0)*2); 这么条语句.在此做个总结. (更好的阅读体验可访问 这里 ) ...

  8. SSI注入--嵌入HTML页面中的指令,类似jsp、asp对现有HTML页面增加动态生成内容,见后面例子

    SSI注入漏洞总结 from:https://www.mi1k7ea.com/2019/09/28/SSI%E6%B3%A8%E5%85%A5%E6%BC%8F%E6%B4%9E%E6%80%BB%E ...

  9. 第15节_BLE协议GATT层

    学习资料:官方手册 Vol 3: Core System Package [Host volume] Part G: Generic Attribute Profile (GATT) 这篇文章格式比较 ...

  10. XML 配置文件,知识点

    namespace 属性:配置成接口的全限定名称,将 Mapper 接口和 XML 文件关联起来: select 标签的 id 属性值,对应定义的接口方法名. insert 标签的属性 paramet ...