dotnet 值拷贝、浅拷贝、深拷贝详解
zhezhongyun 2025-05-14 18:26 51 浏览
简介
在 .NET 中,值拷贝(Value Copy)主要指的是将一个 值类型 的实例或对象的值复制到另一个变量中,使两个变量之间互不影响。我们可以从几个维度来详细理解:
值拷贝的本质
.NET 中的类型分为:
- 值类型(Value Type):例如 int、float、bool、DateTime、struct 自定义结构体
- 引用类型(Reference Type):例如 string(特殊值行为)、class、object、array、List<T> 等
值类型在赋值时是拷贝值本身,引用类型则是拷贝引用(地址)。
值类型是值拷贝
int a = 10;
int b = a; // 值拷贝
b = 20;
Console.WriteLine(a); // 输出 10,不受 b 更改影响
引用类型是引用拷贝
class Person
{
public string Name;
}
Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // 引用拷贝,指向同一对象
p2.Name = "Bob";
Console.WriteLine(p1.Name); // 输出 Bob,p1 也被修改了
自定义结构体也是值类型(值拷贝)
struct Point
{
public int X;
public int Y;
}
Point p1 = new Point { X = 1, Y = 2 };
Point p2 = p1; // 值拷贝
p2.X = 100;
Console.WriteLine(p1.X); // 1
Console.WriteLine(p2.X); // 100
结构体之间的赋值是完全拷贝一份内存,不影响原来的结构体变量。
浅拷贝 vs 深拷贝(类场景)
如果需要让 引用类型 的对象像 值类型 一样“复制”,就要实现:
- 浅拷贝(Shallow Copy):复制对象的字段引用
- 深拷贝(Deep Copy):复制整个对象图结构
使用 MemberwiseClone() 实现浅拷贝(只能在类内部使用)
public class Person
{
public string Name;
public Address Address;
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
}
public class Address
{
public string City;
}
// 使用
var original = new Person { Name = "Alice", Address = new Address { City = "New York" } };
var copy = original.ShallowCopy();
copy.Name = "Bob";
copy.Address.City = "London";
Console.WriteLine(original.Name); // 输出: Alice
Console.WriteLine(original.Address.City); // 输出: London(共享引用)
实现深拷贝的常用方式
- 自己 new 一个新对象,手动复制字段
public class Person
{
public string Name;
public Address Address;
public Person DeepCopy()
{
return new Person
{
Name = this.Name,
Address = new Address { City = this.Address.City }
};
}
}
// 使用
var original = new Person { Name = "Alice", Address = new Address { City = "New York" } };
var copy = original.DeepCopy();
copy.Name = "Bob";
copy.Address.City = "London";
Console.WriteLine(original.Name); // 输出: Alice
Console.WriteLine(original.Address.City); // 输出: New York(独立)
- 使用序列化反序列化(如 Newtonsoft.Json 或 System.Text.Json)
public static T DeepCopy<T>(T obj)
{
string json = JsonSerializer.Serialize(obj);
return JsonSerializer.Deserialize<T>(json);
}
// 使用
var original = new Person { Name = "Alice", Address = new Address { City = "New York" } };
var copy = DeepCopy(original);
copy.Address.City = "London";
Console.WriteLine(original.Address.City); // 输出: New York
- 反射或表达式树:动态生成深拷贝逻辑。
public static object DeepCopyReflection(object original) {
if (original == null) return null;
Type type = original.GetType();
object copy = Activator.CreateInstance(type);
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) {
object value = field.GetValue(original);
if (value is ICloneable cloneable)
field.SetValue(copy, cloneable.Clone());
else
field.SetValue(copy, DeepCopyReflection(value));
}
return copy;
}
- 使用第三方库:DeepCloner 实现深拷贝
using System;
using Force.DeepCloner;
public class Person
{
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string City { get; set; }
}
class Program
{
static void Main()
{
var original = new Person
{
Name = "Alice",
Address = new Address { City = "New York" }
};
// 深拷贝对象
var copy = original.DeepClone();
copy.Name = "Bob";
copy.Address.City = "London";
Console.WriteLine(original.Name); // 输出: Alice
Console.WriteLine(original.Address.City); // 输出: New York(独立)
}
}
- 使用第三方库:DeepCloner 处理复杂对象图
DeepCloner 支持嵌套对象、集合和循环引用的深拷贝。
public class Company
{
public List<Employee> Employees { get; set; }
}
public class Employee
{
public string Name { get; set; }
public Company Company { get; set; }
}
class Program
{
static void Main()
{
var company = new Company();
var employee = new Employee { Name = "Alice", Company = company };
company.Employees = new List<Employee> { employee };
// 深拷贝(处理循环引用)
var companyCopy = company.DeepClone();
companyCopy.Employees[0].Name = "Bob";
Console.WriteLine(company.Employees[0].Name); // 输出: Alice(独立)
}
}
性能优化
DeepCloner 通过缓存和 IL 代码生成实现高性能深拷贝,适合高频调用场景。
基准对比
方法 | 耗时(1000次深拷贝) | 内存占用 |
DeepCloner | ~10 ms | 低 |
Json序列化 | ~200 ms | 高 |
手动深拷贝 | ~5 ms(简单对象) | 低 |
- 使用 AutoMapper 进行深度拷贝
using AutoMapper;
using System;
// 定义源对象类
public class Source
{
public int Id { get; set; }
public string Name { get; set; }
}
// 定义目标对象类
public class Destination
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
// 配置映射
var config = new MapperConfiguration(cfg => cfg.CreateMap<Source, Destination>());
var mapper = config.CreateMapper();
// 创建源对象
var source = new Source { Id = 1, Name = "Example" };
// 进行映射(拷贝)
var destination = mapper.Map<Destination>(source);
Console.WriteLine(#34;Id: {destination.Id}, Name: {destination.Name}");
}
}
- 使用 ExpressMapper 进行深度拷贝
ExpressMapper 是一个高性能的对象映射库,它利用表达式树来生成映射代码,从而实现快速的对象映射和拷贝。它支持复杂的映射规则和自定义转换,并且具有良好的性能表现。
using ExpressMapper;
using System;
// 定义源对象类
public class SourceClass
{
public int Value { get; set; }
}
// 定义目标对象类
public class DestinationClass
{
public int Value { get; set; }
}
class Program
{
static void Main()
{
// 配置映射
Mapper.Register<SourceClass, DestinationClass>();
// 创建源对象
var source = new SourceClass { Value = 10 };
// 进行映射(拷贝)
var destination = Mapper.Map<SourceClass, DestinationClass>(source);
Console.WriteLine(#34;Value: {destination.Value}");
}
}- 上一篇:41 基于Sentinel的限流
- 下一篇:如何用控制台打印日历?
相关推荐
- Python入门学习记录之一:变量_python怎么用变量
-
写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...
- python变量命名规则——来自小白的总结
-
python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...
- Python入门学习教程:第 2 章 变量与数据类型
-
2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...
- 绘制学术论文中的“三线表”具体指导
-
在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...
- Python基础语法知识--变量和数据类型
-
学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...
- 一文搞懂 Python 中的所有标点符号
-
反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...
- Python变量类型和运算符_python中变量的含义
-
别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...
- 从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序
-
在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...
- Python中下划线 ‘_’ 的用法,你知道几种
-
Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...
- 解锁Shell编程:变量_shell $变量
-
引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...
- 一文学会Python的变量命名规则!_python的变量命名有哪些要求
-
目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...
- 更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for
-
src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...
- C++第五课:变量的命名规则_c++中变量的命名规则
-
变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....
- Rust编程-核心篇-不安全编程_rust安全性
-
Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...
- 探秘 Python 内存管理:背后的神奇机制
-
在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- opacity 属性 (32)
- transition 属性 (33)
- 1-1. 变量声明 (31)
