C# 值拷贝、引用拷贝、浅拷贝、深拷贝

值拷贝

定义:直接复制变量的值,适用于基本数据类型(如int, float, char等)。在 C# 中,值类型(基本数据类型和结构体)默认使用值拷贝。

特点:创建原始值的完全独立副本,修改副本不会影响原始值

示例

cs 复制代码
int a = 10;
int b = a; // 值拷贝
b = 20;    // 修改b不会影响a

打印:

引用拷贝

定义:复制对象的引用(内存地址),不创建新对象。对于引用类型(类、接口、委托、数组等),赋值操作默认是引用拷贝。

特点:新变量和原变量指向同一个对象,修改其中任一对象都会影响另一个

示例

cs 复制代码
class Person { public string Name; }
var person1 = new Person() { Name = "小明" };
var person2 = person1;

person2.Name = "小红";

打印:

浅拷贝

定义:创建一个新的对象。如果数据是基本类型,拷贝的就是基本类型的值;如果数据是引用类型,则拷贝的就是内存地址即引用。

特点

1、修改克隆对象中的基本类型数据不会影响原对象的基本类型数据。

2、修改克隆对象中的引用类型数据会影响原对象的值类型数据,本质上,两者的引用类型数据是同一个。

示例

cs 复制代码
class Person : ICloneable
{
    public string Name;
    public int Id;
    public Address Address; // 引用类型字段

    public object Clone()
    {
        return this.MemberwiseClone(); // 浅拷贝
    }
}

class Address { public string Street; }

//--------------------------------------------------------

Person person1 = new Person { 
    Name = "Alice", 
    Id = 666,
    Address = new Address { Street = "北京" }
};

Person person2 = (Person)person1.Clone();//浅拷贝

person2.Name = "Bob"; 
person2.Id = 123;
person2.Address.Street = "上海"; // 会影响p1的Address

打印:

深拷贝

定义:完全复制原始对象及其所有嵌套对象,创建一个独立的,全新的对象,在深拷贝中,原始对象与新对象之间没有任何共享的引用

特点:修改克隆对象中的基本类型数据或者引用类型数据都不会影响原对象的数据,它们是完全独立的。

示例

cs 复制代码
class Person : ICloneable
{
    public string Name;
    public int Id;
    public Address Address; // 引用类型字段

    public object Clone()
    {
        Person cloned = (Person)this.MemberwiseClone();
        cloned.Address = new Address { Street = this.Address.Street }; // 深拷贝引用类型字段
        return cloned;
    }
}

class Address { public string Street; }

//--------------------------------------------------------

Person person1 = new Person { 
    Name = "Alice", 
    Id = 666,
    Address = new Address { Street = "北京" }
};

Person person2 = (Person)person1.Clone();//深拷贝

person2.Name = "Bob"; 
person2.Id = 123;
person2.Address.Street = "上海"; 

打印:

总结

拷贝类型 实现方法 特点 适用场景 性能
值拷贝 直接赋值 创建完全独立副本 值类型(int, float, struct等)
引用拷贝 直接赋值 共享同一对象引用 引用类型(class)的简单传递
浅拷贝 MemberwiseClone 复制值类型字段,共享引用类型字段 对象结构简单或引用字段无需独立 中等
深拷贝 手动实现/序列化 完全独立的对象图 复杂对象需要完全独立副本 较低

知识补充

对复杂对象使用序列化实现深拷贝

本示例使用Newtonsoft实现,也可以使用其他的序列化方法实现

cs 复制代码
using Newtonsoft.Json;
class Person : ICloneable
{
    public string Name;
    public int Id;
    public Address Address; // 引用类型字段

    public object Clone()
    {
        string json = JsonConvert.SerializeObject(this);
        return JsonConvert.DeserializeObject<Person>(json);
    }
}

class Address { public string Street; }
特性 序列化方法 普通手动方法
实现复杂度 简单,一行代码处理整个对象图 复杂,需要为每个引用类型字段手动实现
维护成本 低,对象结构变化不影响拷贝逻辑 高,对象结构变化需同步修改拷贝方法
性能 较低(涉及序列化/反序列化开销) 较高(直接内存操作)
循环引用处理 自动处理 需手动处理,否则会栈溢出
私有字段拷贝 可以拷贝私有字段 只能拷贝可访问字段

处理集合类对象的拷贝

浅拷贝

1. 使用构造函数浅拷贝
cs 复制代码
class Person
{
    public string Name;
    public int Id;
}

List<Person> originalList = new List<Person> { new Person
{
    Name = "Alice" ,Id = 111
    } };

List<Person> shallowCopy = new List<Person>(originalList); // 浅拷贝

// 修改新集合中的元素会影响原集合
shallowCopy[0].Name = "Bob";
2. 使用 LINQ 的 ToList()/ToArray()实现浅拷贝
cs 复制代码
class Person
{
    public string Name;
    public int Id;
}

List<Person> originalList = new List<Person> { new Person
{
    Name = "Alice" ,Id = 111
    } };

List<Person> shallowCopy = originalList.ToList();// 浅拷贝

// 修改新集合中的元素会影响原集合
shallowCopy[0].Name = "Bob";

深拷贝

手动实现

先在Person类中实现深拷贝方法

cs 复制代码
class Person : ICloneable
{
    public string Name;
    public int Id;
    public Address Address; // 引用类型字段

    public object Clone()
    {
        string json = JsonConvert.SerializeObject(this);
        return JsonConvert.DeserializeObject<Person>(json);
    }
}

 List<Person> originalList = new List<Person> { new Person
 {
     Name = "Alice" ,Id = 111
     } };

List<Person> deepCopy = new List<Person>();
foreach (var data in originalList)
{
    deepCopy.Add((Person)data.Clone());
}

deepCopy[0].Name = "Bob";
使用 LINQ + 元素拷贝
cs 复制代码
 List<Person> deepCopy = originalList.Select(p => (Person)p.Clone()).ToList();
相关推荐
LZQqqqqo26 分钟前
WinForm 中 ListView 控件的实战应用与功能拓展
开发语言·microsoft·c#·winform
R-G-B1 小时前
【30】C#实战篇——获取路径下的文件名(不包含路径和扩展名),文件名由连续的数字编号+连续的字母编号组成,并分离出文件名数字部分和英文部分
c#·获取路径下的文件名·不包含路径·去除扩展名·分离出文件名数字和英文部分·连续的数字编号·连续的字母编号
忧郁的蛋~7 小时前
C#中LINQ to DataSet操作及DataTable与LINQ相互转换
开发语言·c#·linq
枫景Maple13 小时前
C#字典Dictionary的内部实现原理
开发语言·c#
chenglin01616 小时前
制造业ERP系统架构设计方案(基于C#生态)
开发语言·系统架构·c#
要记得喝水16 小时前
汇编中常用寄存器介绍
开发语言·汇编·windows·c#·.net
shi578316 小时前
C# 常用的线程同步方式
开发语言·后端·c#
钢铁男儿1 天前
C# 异步编程(GUI程序中的异步操作)
开发语言·c#
weixin_307779131 天前
C#实现Hive到Snowflake数据迁移
开发语言·数据仓库·hive·c#
babytiger1 天前
我的c#用到Newtonsoft.Json.dll,Fleck.dll这两个dll能否打到一个exe 中,而不是一起随着exe拷贝
开发语言·c#·json