LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

C# 里实现原型模式的 3 种方式,你都了解吗?


2025年5月9日 0:44 本文热度 88

前言

嗨,早上好!

想象一下你正在制作蛋糕,如果每次都要从头开始准备原材料并烘焙,那将会非常耗时。

但如果已经有了一个现成的蛋糕作为模板,只需要复制它并根据需要做些小改动,就能节省大量时间。

原型模式就像这个过程,让我们可以快速地创建对象副本,同时保持灵活性和效率。

在 C# 中,实现原型模式非常轻松,来看看有哪些方式吧!

基本结构

  1. Prototype 接口:定义克隆自身的接口。

  2. ConcretePrototype 类:实现 Prototype 接口的具体类。

  3. Client 类:使用 Prototype 接口来克隆具体对象。

传统实现方式

// 1. 定义 Prototype 接口
public abstract class NormalActor
{
    public abstract NormalActor clone();
}

// 2. 实现 Prototype 接口的具体类
public class NormalActorA:NormalActor
{
    public override NormalActor clone()
    {
        Console.WriteLine("NormalActorA is call");
        return (NormalActor)this.MemberwiseClone();
    }
}

// 2. 实现 Prototype 接口的具体类
public class NormalActorB :NormalActor
{
    public override NormalActor clone()
    {
        Console.WriteLine("NormalActorB  was called");
        return (NormalActor)this.MemberwiseClone();
    }
}

// 3. Client 使用 
public class GameSystem
{
    public void Run(NormalActor normalActor)
    {
        NormalActor normalActor1 = normalActor.clone();
        NormalActor normalActor2 = normalActor.clone();
        NormalActor normalActor3 = normalActor.clone();
        NormalActor normalActor4 = normalActor.clone();
        NormalActor normalActor5 = normalActor.clone();
    }
}

GameSystem gameSystem = new GameSystem();
gameSystem.Run(new NormalActorA());

实现 ICloneable 接口方式

传统实现方式需要自己定义 Prototype 接口,实际上,C# 已经帮我们定义了 Prototype 接口了,就是 ICloneable 接口,直接实现它就可以了:

// 1. 实现 Prototype 接口的具体类
public class Person : ICloneable
{
    public string Name { getset; }
    public int Age { getset; }
    public Address Address { getset; } // 引用类型字段

    // 实现ICloneable接口的Clone方法
    public object Clone()
    {
        return this.MemberwiseClone(); // 使用Object的MemberwiseClone方法实现浅拷贝
    }

    public void Display()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address.Street}{Address.City}");
    }
}

public class Address
{
    public string Street { getset; }
    public string City { getset; }
}

// 2. Client 使用
var originalPerson = new Person
{
    Name = "John Doe",
    Age = 30,
    Address = new Address { Street = "123 Main St", City = "New York" }
};

var clonedPerson = (Person)originalPerson.Clone();

// 修改克隆对象的属性
clonedPerson.Name = "Jane Smith";
clonedPerson.Age = 25;
clonedPerson.Address.Street = "456 Oak Ave"// 这会同时修改原始对象的Address

originalPerson.Display(); // 输出: Name: John Doe, Age: 30, Address: 456 Oak Ave, New York
clonedPerson.Display();   // 输出: Name: Jane Smith, Age: 25, Address: 456 Oak Ave, New York

深拷贝实现

以上的实现方式非常简单,但有一个问题,就是实现的是浅拷贝,只能复制对象本身以及其中的值类型字段,对于引用类型字段,就只复制引用而不复制引用的对象,这样一旦引用类型字段被修改了,就会影响到其它地方的使用,这在上面的例子中也可以感受到,所以需要创建一个完全独立的副本,即深拷贝实现。

深拷贝的实现主要有两种方法:

  • 序列化/反序例化

  • 手动复制所有字段(虽然比较笨,但对象字段比较少且比较固定时,也不失为一个好方法)

using System;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

[Serializable// 需要标记为可序列化
public class Person : ICloneable
{
    public string Name { getset; }
    public int Age { getset; }
    public Address Address { getset; }

    // 深拷贝实现
    public object Clone()
    {
        // 使用序列化和反序列化实现深拷贝
        using (var memoryStream = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            return formatter.Deserialize(memoryStream);
        }
    }

    // 另一种深拷贝实现方式(手动复制所有字段)
    // public Person DeepCopy()
    // {
    //     var copy = (Person)this.MemberwiseClone();
    //     copy.Address = new Address
    //     {
    //         Street = this.Address.Street,
    //         City = this.Address.City
    //     };
    //     return copy;
    // }

    public void Display()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}, Address: {Address.Street}{Address.City}");
    }
}

[Serializable]
public class Address
{
    public string Street { getset; }
    public string City { getset; }
}

// 使用示例
var originalPerson = new Person
{
    Name = "John Doe",
    Age = 30,
    Address = new Address { Street = "123 Main St", City = "New York" }
};

// 使用序列化方式的深拷贝
var clonedPerson = (Person)originalPerson.Clone();

// 或者使用手动实现的深拷贝
// var clonedPerson = originalPerson.DeepCopy();

// 修改克隆对象的属性
clonedPerson.Name = "Jane Smith";
clonedPerson.Age = 25;
clonedPerson.Address.Street = "456 Oak Ave"// 不会影响原始对象

originalPerson.Display(); // 输出: Name: John Doe, Age: 30, Address: 123 Main St, New York
clonedPerson.Display();   // 输出: Name: Jane Smith, Age: 25, Address: 456 Oak Ave, New York

总结

相比于每次都创建新对象,利用原型模式复制现有对象通常更快。

在文档编辑、缓存系统、配置管理和图形用户界面(GUI)开发等业务场景,原型模式都能发挥很大作用!


阅读原文:


该文章在 2025/5/9 12:11:08 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved