C#原型模式实战:浅拷贝与深拷贝详解

article/2025/6/8 19:47:49

文章目录

    • 一、原型模式简介
    • 二、原型模式的定义与结构
      • 2.1 定义
      • 2.2 结构图
      • 2.3 主要角色
    • 三、C#中的原型模式实现
      • 3.1 使用ICloneable接口
      • 3.2 自定义克隆接口
    • 四、浅拷贝与深拷贝
      • 4.1 浅拷贝(Shallow Copy)
      • 4.2 深拷贝(Deep Copy)
      • 4.3 浅拷贝与深拷贝的比较
    • 五、原型模式适用场景
    • 六、实际应用示例
      • 6.1 文档编辑器中的复制功能
      • 6.2 游戏中的角色原型
    • 七、原型模式的优缺点
      • 7.1 优点
      • 7.2 缺点
    • 八、原型模式与其他设计模式的关系
    • 九、总结
    • 学习资源

一、原型模式简介

原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过实例化类来创建。这种模式特别适用于当对象的创建过程复杂或成本较高时,通过克隆现有对象来提高效率。

原型模式的核心思想来源于现实生活中的"复制"行为。就像孙悟空拔下猴毛变出许多分身一样,原型模式允许我们基于一个原型对象快速创建多个相似的对象。

二、原型模式的定义与结构

2.1 定义

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

2.2 结构图

«interface»
IPrototype
Clone()
ConcretePrototypeA
field1 : Type
field2 : Type
Clone()
ConcretePrototypeB
field1 : Type
field2 : Type
Clone()
Client
Operation()

2.3 主要角色

  1. 抽象原型(Prototype):声明一个克隆自身的接口
  2. 具体原型(ConcretePrototype):实现克隆的具体操作
  3. 客户端(Client):通过调用原型的克隆方法来获取新的对象

三、C#中的原型模式实现

在C#中,实现原型模式通常有两种方式:

  1. 使用C#内置的ICloneable接口
  2. 自定义克隆接口和方法

3.1 使用ICloneable接口

C#框架提供了ICloneable接口,该接口包含一个Clone()方法,用于创建对象的副本。

using System;// 实现ICloneable接口的具体原型类
public class Person : ICloneable
{public string Name { get; set; }public int Age { get; set; }public Address HomeAddress { get; set; }public Person(string name, int age, Address address){Name = name;Age = age;HomeAddress = address;}// 实现浅拷贝public object Clone(){// MemberwiseClone()是Object类的一个保护方法,它创建当前对象的浅表副本return this.MemberwiseClone();}// 实现深拷贝public Person DeepClone(){Person clone = (Person)this.MemberwiseClone();// 手动创建引用类型的新实例clone.HomeAddress = new Address(HomeAddress.Street, HomeAddress.City, HomeAddress.ZipCode);return clone;}
}public class Address
{public string Street { get; set; }public string City { get; set; }public string ZipCode { get; set; }public Address(string street, string city, string zipCode){Street = street;City = city;ZipCode = zipCode;}
}// 客户端代码
class Program
{static void Main(string[] args){// 创建原型对象Address address = new Address("123 Main St", "Beijing", "100000");Person person1 = new Person("张三", 30, address);// 浅拷贝Person person2 = (Person)person1.Clone();person2.Name = "李四";person2.HomeAddress.Street = "456 Elm St"; // 注意:这会改变person1的地址!// 深拷贝Person person3 = person1.DeepClone();person3.Name = "王五";person3.HomeAddress.Street = "789 Oak St"; // 不会影响person1的地址// 输出结果Console.WriteLine($"Person1: {person1.Name}, Address: {person1.HomeAddress.Street}");Console.WriteLine($"Person2: {person2.Name}, Address: {person2.HomeAddress.Street}");Console.WriteLine($"Person3: {person3.Name}, Address: {person3.HomeAddress.Street}");}
}

3.2 自定义克隆接口

using System;// 自定义的原型接口
public interface IPrototype<T>
{T Clone();
}// 具体原型类
public class Document : IPrototype<Document>
{public string Title { get; set; }public string Content { get; set; }public DocumentInfo Info { get; set; }public Document(string title, string content, DocumentInfo info){Title = title;Content = content;Info = info;}// 浅拷贝public Document Clone(){return (Document)this.MemberwiseClone();}// 深拷贝public Document DeepClone(){Document clone = (Document)this.MemberwiseClone();// 手动创建DocumentInfo的新实例clone.Info = new DocumentInfo(Info.Author, Info.CreationDate);return clone;}
}public class DocumentInfo
{public string Author { get; set; }public DateTime CreationDate { get; set; }public DocumentInfo(string author, DateTime creationDate){Author = author;CreationDate = creationDate;}
}// 客户端代码
class Program
{static void Main(string[] args){// 创建原型对象DocumentInfo info = new DocumentInfo("张三", DateTime.Now);Document doc1 = new Document("设计模式", "原型模式的内容...", info);// 浅拷贝Document doc2 = doc1.Clone();doc2.Title = "设计模式副本";doc2.Info.Author = "李四"; // 修改会影响doc1// 深拷贝Document doc3 = doc1.DeepClone();doc3.Title = "设计模式另一副本";doc3.Info.Author = "王五"; // 不会影响doc1// 输出结果Console.WriteLine($"Doc1: {doc1.Title}, Author: {doc1.Info.Author}");Console.WriteLine($"Doc2: {doc2.Title}, Author: {doc2.Info.Author}");Console.WriteLine($"Doc3: {doc3.Title}, Author: {doc3.Info.Author}");}
}

四、浅拷贝与深拷贝

在原型模式中,理解浅拷贝和深拷贝的区别至关重要:

4.1 浅拷贝(Shallow Copy)

浅拷贝只复制对象的值类型字段和引用类型字段的引用,而不复制引用类型字段所指向的对象。

  • C#中可以通过Object.MemberwiseClone()方法实现
  • 优点:实现简单,性能高
  • 缺点:当原型对象中包含引用类型字段时,克隆对象与原型对象会共享这些引用类型的实例

4.2 深拷贝(Deep Copy)

深拷贝不仅复制对象的值类型字段和引用类型字段的引用,还递归地复制所有引用类型字段所指向的对象。

  • 实现方式:
    1. 手动创建所有引用类型成员的新实例
    2. 通过序列化和反序列化(适合复杂对象)
// 使用序列化实现深拷贝
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;[Serializable]
public class Employee : ICloneable
{public string Name { get; set; }public Department Department { get; set; }public object Clone(){return this.MemberwiseClone(); // 浅拷贝}public Employee DeepClone(){// 使用序列化和反序列化实现深拷贝using (MemoryStream stream = new MemoryStream()){BinaryFormatter formatter = new BinaryFormatter();formatter.Serialize(stream, this);stream.Position = 0;return (Employee)formatter.Deserialize(stream);}}
}[Serializable]
public class Department
{public string Name { get; set; }public string Location { get; set; }
}

注意:使用BinaryFormatter进行序列化在.NET 5+版本中被标记为过时,建议在实际开发中使用其他序列化方式,如JSON序列化。

4.3 浅拷贝与深拷贝的比较

特性浅拷贝深拷贝
性能较低
实现复杂度简单复杂
内存占用
适用场景对象中只有值类型或不关心引用类型的共享需要完全独立的对象副本

五、原型模式适用场景

  1. 对象创建成本高:当对象的创建过程很耗资源(如需要访问数据库、读取大文件等)时,克隆已有对象更高效。

  2. 系统需要独立于产品创建过程:客户端不需要知道对象创建的细节,只需要一个原型对象的引用。

  3. 对象类型在运行时确定:当系统需要在运行时确定创建哪种类型的对象时,原型模式特别有用。

  4. 需要大量相似对象:如游戏中的相似角色、配置系统中的相似配置等。

  5. 需要保存对象的特定状态:有时候我们需要保存对象的某个状态,以便将来能够恢复到这个状态。

六、实际应用示例

6.1 文档编辑器中的复制功能

using System;
using System.Collections.Generic;// 文档元素接口
public interface IDocumentElement : ICloneable
{void Render();
}// 具体元素:文本
public class TextElement : IDocumentElement
{public string Content { get; set; }public string FontFamily { get; set; }public int FontSize { get; set; }public TextElement(string content, string fontFamily, int fontSize){Content = content;FontFamily = fontFamily;FontSize = fontSize;}public object Clone(){return new TextElement(Content, FontFamily, FontSize);}public void Render(){Console.WriteLine($"渲染文本: '{Content}', 字体: {FontFamily}, 大小: {FontSize}pt");}
}// 具体元素:图像
public class ImageElement : IDocumentElement
{public string Source { get; set; }public int Width { get; set; }public int Height { get; set; }public ImageElement(string source, int width, int height){Source = source;Width = width;Height = height;}public object Clone(){return new ImageElement(Source, Width, Height);}public void Render(){Console.WriteLine($"渲染图像: {Source}, 尺寸: {Width}x{Height}px");}
}// 文档类
public class Document : ICloneable
{public string Title { get; set; }public List<IDocumentElement> Elements { get; set; } = new List<IDocumentElement>();public void AddElement(IDocumentElement element){Elements.Add(element);}public void Render(){Console.WriteLine($"文档: {Title}");Console.WriteLine("内容:");foreach (var element in Elements){element.Render();}}public object Clone(){Document newDoc = new Document { Title = $"{Title} - 副本" };// 深拷贝所有元素foreach (var element in Elements){newDoc.AddElement((IDocumentElement)element.Clone());}return newDoc;}
}// 客户端代码
class Program
{static void Main(string[] args){// 创建原始文档Document originalDoc = new Document { Title = "设计模式研究" };originalDoc.AddElement(new TextElement("原型模式介绍", "Arial", 12));originalDoc.AddElement(new ImageElement("diagram.png", 800, 600));originalDoc.AddElement(new TextElement("原型模式适用于...", "Times New Roman", 10));// 渲染原始文档originalDoc.Render();Console.WriteLine();// 克隆文档Document clonedDoc = (Document)originalDoc.Clone();// 修改克隆文档((TextElement)clonedDoc.Elements[0]).Content = "原型模式深入研究";// 渲染克隆文档clonedDoc.Render();}
}

6.2 游戏中的角色原型

using System;// 角色接口
public interface ICharacter
{ICharacter Clone();void Display();
}// 具体角色:战士
public class Warrior : ICharacter
{public string Name { get; set; }public int Health { get; set; }public int Attack { get; set; }public string Weapon { get; set; }public Warrior(string name, int health, int attack, string weapon){Name = name;Health = health;Attack = attack;Weapon = weapon;}public ICharacter Clone(){return new Warrior(Name, Health, Attack, Weapon);}public void Display(){Console.WriteLine($"战士: {Name}, 生命值: {Health}, 攻击力: {Attack}, 武器: {Weapon}");}
}// 具体角色:法师
public class Mage : ICharacter
{public string Name { get; set; }public int Health { get; set; }public int Mana { get; set; }public string Spell { get; set; }public Mage(string name, int health, int mana, string spell){Name = name;Health = health;Mana = mana;Spell = spell;}public ICharacter Clone(){return new Mage(Name, Health, Mana, Spell);}public void Display(){Console.WriteLine($"法师: {Name}, 生命值: {Health}, 魔法值: {Mana}, 法术: {Spell}");}
}// 角色原型管理器
public class CharacterManager
{private readonly Dictionary<string, ICharacter> _characterPrototypes = new Dictionary<string, ICharacter>();public void AddPrototype(string key, ICharacter character){_characterPrototypes[key] = character;}public ICharacter GetPrototype(string key){if (!_characterPrototypes.ContainsKey(key)){throw new ArgumentException($"没有找到键为'{key}'的原型");}return _characterPrototypes[key].Clone();}
}// 客户端代码
class Program
{static void Main(string[] args){// 创建原型管理器CharacterManager manager = new CharacterManager();// 注册原型manager.AddPrototype("elite_warrior", new Warrior("精英战士", 200, 30, "大刀"));manager.AddPrototype("elite_mage", new Mage("精英法师", 100, 150, "火球术"));// 从原型创建实例并自定义ICharacter warrior1 = manager.GetPrototype("elite_warrior");((Warrior)warrior1).Name = "战士1";ICharacter warrior2 = manager.GetPrototype("elite_warrior");((Warrior)warrior2).Name = "战士2";((Warrior)warrior2).Weapon = "巨斧";ICharacter mage1 = manager.GetPrototype("elite_mage");((Mage)mage1).Name = "法师1";// 显示所有角色warrior1.Display();warrior2.Display();mage1.Display();}
}

七、原型模式的优缺点

7.1 优点

  1. 减少子类创建:原型模式不需要创建与产品层次相同的工厂层次。
  2. 提高性能:克隆对象通常比创建新对象更高效。
  3. 动态添加和删除对象:可以在运行时动态添加或删除原型。
  4. 隐藏具体产品类:客户端无需知道具体产品类的信息。
  5. 复制复杂对象:可以复制具有复杂内部结构的对象。

7.2 缺点

  1. 深拷贝复杂度:实现深拷贝可能会很复杂,特别是对象包含循环引用时。
  2. 克隆方法实现困难:对每个类都需要实现克隆方法,对于已有的类改造可能比较困难。
  3. 不支持final字段克隆:在某些语言中,不支持克隆包含final字段的对象。

八、原型模式与其他设计模式的关系

  1. 与工厂模式的关系:原型模式通常与工厂方法模式结合使用,工厂方法创建原型实例,然后通过原型的克隆方法创建新对象。

  2. 与建造者模式的关系:建造者模式关注的是"如何一步一步构建一个复杂对象",而原型模式关注的是"如何复制一个已有对象"。

  3. 与命令模式的关系:在实现撤销/重做功能时,常常需要保存对象状态的快照,这时可以使用原型模式。

九、总结

原型模式通过克隆现有对象来创建新对象,避免了重新初始化的过程,特别适合对象创建成本高或需要保存对象状态的场景。在C#中,我们可以通过实现ICloneable接口或自定义接口来实现原型模式,同时需要注意浅拷贝和深拷贝的使用场景与实现方式。

原型模式的关键在于理解何时使用浅拷贝,何时使用深拷贝,以及如何正确地实现它们。合理应用原型模式,可以使我们的代码更加灵活、高效。

学习资源

  • Microsoft Docs: ICloneable Interface
  • Refactoring Guru: Prototype Pattern
  • Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides

在这里插入图片描述


http://www.hkcw.cn/article/ueetDxcjFg.shtml

相关文章

统计图中节点特征的余弦相似度、欧式距离

把有关系节点的余弦相似度保存在一块&#xff0c;再把没有关系节点的余弦相似度保存在一块&#xff0c;最后绘图 程序&#xff1a; #zhouzhichao #25年5月29日 #统计图中节点的余弦相似度import h5py import random import numpy as np import pandas as pd from sklearn.met…

小沈阳演唱会嘉宾是女儿 同台献唱温馨互动

舞台灯光在夜空中划出几道银白的弧线,沈阳奥体中心的欢呼声浪一阵高过一阵。小沈阳穿着标志性的格子衬衫,手里的麦克风还没凑近唇边,台下就爆发出此起彼伏的“阳仔——”呼喊。他笑着转了个圈,眼角余光不经意扫向后台侧幕——那里站着个扎着高马尾的姑娘,藏蓝色牛仔外套下…

小米王化疑回怼余承东 暗讽友商根底浅

6月1日,小米的王化发文提到,到2025年5月,小米汽车的交付量将超过28000台。他还表示,公司正在为小米YU7的大规模量产做准备,并感谢了大家的支持与青睐。他引用了一句名言:墙上芦苇,头重脚轻根底浅;山间竹笋,嘴尖皮厚腹中空。责任编辑:zx0001

处理链--条件判断

在同一条链里&#xff0c;根据条件判断下一步到底执行不执行。那就得用到下面这个条件判断的process。这个就负责条件分支执行&#xff0c;在处理链运行时按逻辑结果来把处理链分成不同的分支执行。 很简单&#xff0c;先把它拖到处理链里。 然后就可以来定义条件。条件的定义决…

c++5月31日笔记

题目&#xff1a;水龙头 时间限制&#xff1a;C/C 语言 1000MS&#xff1b;其他语言 3000MS 内存限制&#xff1a;C/C 语言 65536KB&#xff1b;其他语言 589824KB 题目描述&#xff1a; 小明在 0 时刻&#xff08;初始时刻&#xff09;将一个空桶放置在漏水的水龙头下。已知桶…

乒协支持樊振东加盟德甲 征战德甲与欧冠

6月1日,德甲萨尔布吕肯俱乐部宣布中国运动员樊振东加盟,他将参加2025-2026赛季的德国乒乓球甲级联赛和欧洲冠军联赛。目前樊振东仍处于奥运后的调整期,计划通过全国比赛及国内外俱乐部比赛逐步恢复状态。中国乒协对此表示理解和支持,将全力保障樊振东保持良好竞技水平,为乒…

罗翔现场普法 沃尔沃全新S90上市 北欧豪华领航而至

2025年5月29日,沃尔沃全新S90在大庆工厂正式上市。新车共推出7个配置版本,限时参考尊享价30.09万元起,并提供价值超过85,000元的八重惊喜礼。沃尔沃全新S90致力于为全球有品位、追求品质的精英人群带来进阶焕新的豪华体验。沃尔沃汽车品牌挚友罗翔出席发布会,以法律专家的角…

向佐六一晒女儿 网友:幸好颜值随妈 父女温馨互动登热搜

2025年6月1日中午12点左右,向佐在个人微博上首次公开了4岁女儿向芷(乳名“小奶皇”)的正脸照片及互动视频。这位一向注重孩子隐私保护的父亲,在儿童节这个特殊日子与网友分享了父女间的温馨时刻,迅速登上热搜榜首。视频中,小奶皇认真地为爸爸做面部按摩,肉嘟嘟的小手在向…

JS和TS的区别

JavaScript 与 TypeScript 的主要区别和特性对比 1. 基础定义 JavaScript 是一种动态、弱类型的编程语言&#xff0c;广泛应用于前端开发以及通过 Node.js 扩展到后端开发。TypeScript 则是 JavaScript 的超集&#xff0c;它在 JavaScript 的基础上添加了静态类型系统和其他增…

淘晶驰串口屏开发初体验:10分钟实现简易加法器

目录 一、开发环境闪电搭建&#xff08;3分钟&#xff09; 二、加法计算器实现步骤&#xff08;7分钟&#xff09; 1.工程创建 2.创建字库和导入字库 3. 拖放控件-文本控件 4.拖放控件-数字控件 5.&#xff08;x&#xff09;变量 6.按钮 三、测试 在嵌入式设备的人机交…

CppCon 2014 学习:Parallelizing the Standard Algorithms Library

Bringing Parallelism to C&#xff08;为 C 引入并行性&#xff09; 技术规范&#xff08;Technical Specification&#xff09; 是对 C 标准库的一个扩展草案&#xff08;TS Technical Specification&#xff09;。不会改变现有代码语义&#xff0c;而是提供新功能。专门针…

俄罗斯一桥梁坍塌致3死28伤 爆炸装置所致

俄罗斯布良斯克州一座桥梁发生坍塌,导致一列由莫斯科开往该州城市克利莫沃的火车脱轨。据俄罗斯紧急情况部初步统计,事故共造成31人伤亡,其中3人不幸遇难,28人已送往医疗机构救治。此前有报道指出,事件造成4人死亡,至少44人受伤。俄紧急情况部表示,救援人员正在对受损车…

任务调度器:从入门到放弃(二)

理想与现实的差异 在上文中&#xff0c;我们提到&#xff0c;一个优先级100的线程&#xff0c;跟一个优先级为120的线程进行公平调度的资源抢占的话。 从抓取的一份systrace上简单的对比来看&#xff0c; 其份额大概是24:1856 &#xff08;systrace上随便框的&#xff0c;数据…

linux 1.0.6

PMS和软件安装的介绍 软件安装不能放在前面 你连linux自带的命令都整不会&#xff0c;你还去使用人家自己写的软件 整一个类似于手机商城的概念&#xff0c;直接进商城就可以下载软件 package management system 也就是PMS 叫做包管理器&#xff0c;作用是用来进行软件的安装&…

3D Gaussian splatting 02: 快速评估

目录 3D Gaussian splatting 01: 环境搭建3D Gaussian splatting 02: 快速评估3D Gaussian splatting 03: 用户数据训练和结果查看3D Gaussian splatting 04: 代码阅读-提取相机位姿和稀疏点云3D Gaussian splatting 05: 代码阅读-训练整体流程3D Gaussian splatting 06: 代码阅…

【系统配置与部署类】linux系统下的desktop图标文件配置

相关文章已经在个人博客网站上更新&#xff0c;欢迎访问&#xff1a; linux系统下的desktop图标文件配置http://www.turnin-blog.online/articles/%E7%B3%BB%E7%BB%9F%E9%85%8D%E7%BD%AE%E4%B8%8E%E9%83%A8%E7%BD%B2/linux%E7%B3%BB%E7%BB%9F%E4%B8%8B%E7%9A%84desktop%E5%9B%…

法国为何此时回应阵风是否被击落 服役20年首损?

法国军方首次回应“阵风”战机疑被击落事件,称如果消息属实,这将是该机服役以来首次在实战中损毁。印度空军引进了这款战斗机。5月27日,在例行记者会上,法国国防部军方发言人强调,若相关信息属实,这将是“阵风”战斗机服役20年来首次在实战中损毁。这也是法国军方就这一话…

从零开始的二三维CAD|CAE软件: 解决VTK,DICOM体素化-失效问题.

背景: 在从零开始的二三维软件开发中, 需要加载CT的dicoms影像文件, 并将其序列化之后的数据,体素化 可惜..vtk的c#库,将其体素化的时候,竟然失败... 使用vtkDicomReader ,设置 Dicom文件夹读取,竟然不停的失败...从网上找了一些版本.也没啥可用的资料... 解决办法: 直接…

Cesium快速入门到精通系列教程一

一、打造第一个Cesium应用 1、官方渠道下载Cesium&#xff08;可选择历史版本&#xff09; ​​GitHub Releases页面​​ 访问 Cesium GitHub Releases&#xff0c;此处列出了所有正式发布的版本。 通过标签&#xff08;如 v1.95.0&#xff09;选择目标版本&#xff0c;下载…

【多线程初阶】synchronized -监视器锁monitor lock

文章目录 &#x1f305;synchronized关键字&#x1f30a; synchronized 的互斥&#x1f30a; synchronized 的变种写法&#x1f3c4;‍♂️synchronized 修饰代码块 :明确指定锁哪个对象&#x1f3c4;‍♂️synchronized 修饰方法 &#x1f30a; synchronized 的可重入性&#…