阿杰,我来把 .NET 深拷贝(Deep Copy) 给你系统讲透,不仅告诉你原理,还会给你常用的几种实现方式和注意事项。


1. 深拷贝 vs 浅拷贝

类型特点引用类型字段处理场景
浅拷贝(Shallow Copy)只复制对象本身及其值类型字段复制引用的地址(两个对象共享同一引用数据)临时复制,原对象改引用内容会影响新对象
深拷贝(Deep Copy)递归复制整个对象,包括它引用的子对象每个引用字段都会创建新的副本数据隔离、并发安全

例子:

class Person { public string Name; }
var p1 = new Person { Name = "阿杰" };
var p2 = p1; // 浅拷贝:只是复制引用
p2.Name = "小李"; // p1.Name 也变了

// 深拷贝:p2 是新对象,修改不会影响 p1

2. 浅拷贝的内置方式(只是对比)

Person p2 = (Person)p1.MemberwiseClone();

MemberwiseClone() 是受保护的(protected),只能在类内部调用。它不递归拷贝引用类型,所以只是浅拷贝。


3. 深拷贝的常见实现方法

方法 1:手动递归复制

适合字段少、结构简单的对象。

public class Person
{
    public string Name;
    public Address Addr;

    public Person DeepCopy()
    {
        return new Person
        {
            Name = this.Name, // string 是不可变类型,直接赋值即可
            Addr = new Address { City = this.Addr.City }
        };
    }
}

public class Address
{
    public string City;
}

✅ 优点:性能高、可控
❌ 缺点:字段多、层级深时很繁琐


方法 2:利用序列化(BinaryFormatter / DataContract / JSON)

适合快速实现,自动处理嵌套引用。

2.1 二进制序列化(旧方法,不推荐在新项目中)

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

public static T DeepClone<T>(T obj)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, obj);
        ms.Position = 0;
        return (T)formatter.Deserialize(ms);
    }
}

⚠️ 需要 [Serializable] 特性,.NET 5+ 已弃用 BinaryFormatter


2.2 JSON 序列化(推荐)

System.Text.JsonNewtonsoft.Json

using System.Text.Json;

public static T DeepClone<T>(T obj)
{
    var json = JsonSerializer.Serialize(obj);
    return JsonSerializer.Deserialize<T>(json);
}

✅ 不需要 [Serializable]
❌ 性能比手动拷贝差,不支持循环引用(除非特殊配置)


方法 3:反射拷贝

动态遍历所有字段并递归拷贝。

using System;
using System.Reflection;

public static class DeepCopyHelper
{
    public static T DeepCopy<T>(T obj)
    {
        if (obj == null) return default;
        var type = obj.GetType();

        // 值类型或字符串直接返回
        if (type.IsValueType || type == typeof(string))
            return obj;

        // 创建新实例
        var result = Activator.CreateInstance(type);
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
        {
            var fieldValue = field.GetValue(obj);
            field.SetValue(result, DeepCopy(fieldValue));
        }
        return (T)result;
    }
}

✅ 自动化,不用写每个字段
❌ 性能较低,不支持某些特殊类型(如未实现无参构造)


方法 4:AutoMapper(第三方库)

快速把一个对象映射成另一个对象。

using AutoMapper;

var config = new MapperConfiguration(cfg => cfg.CreateMap<Person, Person>());
var mapper = config.CreateMapper();
Person p2 = mapper.Map<Person>(p1);

✅ 简洁,尤其适合 DTO ↔ Entity 转换
❌ 配置麻烦,依赖外部库


4. 循环引用问题

深拷贝时如果对象 A 引用对象 B,而 B 又引用回 A,会导致递归死循环。
解决方法:

  • 用字典记录已拷贝的对象,重复引用直接返回现有副本
  • 序列化时开启循环引用处理(如 Newtonsoft.Json 的 PreserveReferencesHandling

5. 性能对比(大致趋势)

方法性能可维护性限制
手动写拷贝⭐⭐⭐⭐⭐要改代码
反射递归⭐⭐
JSON 序列化⭐⭐循环引用需处理
AutoMapper⭐⭐⭐需配置