原型模式属于创建型设计模式,核心思想是通过复制已有对象来生成新实例,从而避免重复执行昂贵的初始化逻辑。在C#里,所有引用类型都隐式继承自System.Object,而Object提供了一个受保护的MemberwiseClone方法,这成了实现克隆的天然入口。理解它与ICloneable接口的配合方式,是写好原型模式的关键。

一、MemberwiseClone的浅拷贝本质
MemberwiseClone是Object的受保护方法,它创建当前对象的一个新实例,然后将当前对象的所有非静态字段逐位复制过去。对于值类型字段,复制的是实际数据;对于引用类型字段,复制的仅仅是引用地址,也就是说原对象和克隆对象会共享同一个引用对象。
下面这段代码展示了浅拷贝带来的副作用:修改克隆体的引用类型字段,原对象也跟着变了。这种特性在需要独立副本的业务里是隐患,比如缓存模板对象被多个请求复用后又被意外改写。
using System;
public class Address
{
public string City;
}
public class Person
{
public string Name;
public int Age;
public Address Address;
public Person ShallowCopy()
{
// 调用Object的受保护方法做浅拷贝
return (Person)this.MemberwiseClone();
}
}
class Program
{
static void Main()
{
Person p1 = new Person { Name = "Tom", Age = 20, Address = new Address { City = "Beijing" } };
Person p2 = p1.ShallowCopy();
p2.Address.City = "Shanghai";
Console.WriteLine(p1.Address.City); // 输出 Shanghai,原对象被污染
}
}
二、ICloneable接口与规范克隆
ICloneable是.NET提供的标准克隆接口,只定义了一个Clone方法。让业务类实现该接口,可以把克隆逻辑收敛到类内部,调用方不需要关心是深拷贝还是浅拷贝。不过官方文档也提醒,ICloneable并没有规定Clone必须返回深拷贝还是浅拷贝,因此团队内部需要约定清楚。
我们在Person类上实现ICloneable,并在Clone方法里手动创建引用类型的新实例,从而实现深拷贝。这种方式直观可控,也能顺带处理字段重命名的兼容问题。
using System;
public class Address
{
public string City;
public Address Clone()
{
return new Address { City = this.City };
}
}
public class Person : ICloneable
{
public string Name;
public int Age;
public Address Address;
public object Clone()
{
Person copy = (Person)this.MemberwiseClone();
copy.Address = this.Address.Clone(); // 手动复制引用对象
return copy;
}
}
class Program
{
static void Main()
{
Person p1 = new Person { Name = "Tom", Age = 20, Address = new Address { City = "Beijing" } };
Person p2 = (Person)p1.Clone();
p2.Address.City = "Shanghai";
Console.WriteLine(p1.Address.City); // 输出 Beijing,原对象安全
}
}
三、通用深拷贝的几种实现思路
当对象图很复杂时,手写每个引用类型的Clone容易遗漏。此时可以用序列化反序列化方式做通用深拷贝:把对象写成二进制或JSON流再读回来,得到的一定是全新对象树。这种写法对类有要求,比如字段要可序列化、无循环引用。
另一种思路是用反射递归遍历属性赋值,不依赖序列化特性,但性能比手写克隆差。下面给出基于二进制序列化的深拷贝辅助方法,适用于标记了Serializable的类型。
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
public class Address
{
public string City;
}
[Serializable]
public class Person
{
public string Name;
public Address Address;
}
public static class DeepCloneHelper
{
public static T DeepClone<T>(T obj)
{
if (obj == null) return default(T);
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(ms);
}
}
}
四、原型模式在业务中的落地建议
在订单、报表等模板化场景,原型模式能明显减少重复构建开销。建议把克隆方法做成受保护或显式接口,避免外部随意浅拷贝。对于包含非托管资源或事件委托的对象,克隆时要额外断开事件绑定,防止副本收到原对象通知。
性能方面,手写Clone优于反射和序列化;若对象极小且创建成本低,直接new可能比克隆更清晰。选择方案时权衡对象复杂度与调用频率,才能让原型模式真正提升代码可维护性。
| 方案 | 速度 | 适用场景 |
|---|---|---|
| MemberwiseClone浅拷贝 | 最快 | 无引用类型字段或允许共享 |
| 手写ICloneable深拷贝 | 快 | 对象图稳定、追求性能 |
| 序列化深拷贝 | 慢 | 通用工具、类型多变 |
C#ICloneableMemberwiseClone修改时间:2026-08-08 10:57:13