C#中的浅拷贝与深拷贝

浅拷贝

值类型

值类型的拷贝是将源拷贝对象的值赋予被拷贝对象,在修改被拷贝对象的值之后并不会影响源拷贝对象的值

1
2
3
4
5
6
7
8
public static void Main(string[] args)
{
//值类型拷贝
int a = 5, b = a;
b = 6;
Console.WriteLine($"a:{a}");
Console.WriteLine($"b:{b}");
}

控制台输出结果:

1
2
a:5
b:6

引用类型

引用类型的拷贝只是复制源拷贝对象的引用地址给被拷贝对象,两者的引用地址都指向托管堆上的同一处对象实例,因此在修改被拷贝对象后源拷贝对象也会被修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static void Main(string[] args)
{
Student A = new Student("张三", "男");
Student B = new Student();
B = A;
Console.WriteLine($"A:{A.ToString()}");
Console.WriteLine($"B:{B.ToString()}");
B.Name = "李四";
B.Gender = "女";
Console.WriteLine($"A:{A.ToString()}");
Console.WriteLine($"B:{B.ToString()}");
Console.ReadKey();
}

public class Student
{
public Student() { }
public Student(string name,string gender)
{
this.Name = name;
this.Gender = gender;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}

private string _gender;
public string Gender
{
get { return _gender; }
set { _gender = value; }
}

public override string ToString()
{
return $"Name:{Name},Gender:{Gender}";
}
}



控制台输出结果:

1
2
3
4
A:Name:张三,Gender:男
B:Name:张三,Gender:男
A:Name:李四,Gender:女
B:Name:李四,Gender:女

浅拷贝亦可继承自接口ICloneable通过MemberwiseClone方法实现接口的Clone成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static void Main(string[] args)
{
Student A = new Student("张三", "男");
Student B = (Student)A.Clone();
Console.WriteLine($"A:{A.ToString()}");
Console.WriteLine($"B:{B.ToString()}");
Console.ReadKey();
}

public class Student:ICloneable
{
public Student() { }
public Student(string name,string gender)
{
this.Name = name;
this.Gender = gender;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}

private string _gender;
public string Gender
{
get { return _gender; }
set { _gender = value; }
}

public override string ToString()
{
return $"Name:{Name},Gender:{Gender}";
}

public object Clone()
{
//创建浅表副本
return this.MemberwiseClone();
}
}

深拷贝

通过复制对象的每个成员实现深拷贝(仍以Student为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public static void Main(string[] args)
{
Student A = new Student("张三", "男");
//Student B = new Student();
//B.Name = A.Name;
//B.Gender = A.Gender;
//Console.WriteLine($"A:{A.ToString()}");
//Console.WriteLine($"B:{B.ToString()}");
//B.Name = "李四";
//B.Gender = "女";
//Console.WriteLine($"A:{A.ToString()}");
//Console.WriteLine($"B:{B.ToString()}");

Student B = DeepCopyByReflection<Student>(A);
Console.WriteLine($"A:{A.ToString()}");
Console.WriteLine($"B:{B.ToString()}");
B.Name = "李四";
B.Gender = "女";
Console.WriteLine($"A:{A.ToString()}");
Console.WriteLine($"B:{B.ToString()}");
}

/// <summary>
/// 复制对象每个成员实现深拷贝
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopyByReflection<T>(T obj)
{
if (obj is string || obj.GetType().IsValueType)
return obj;

object retval = Activator.CreateInstance(obj.GetType());//创建对象实例
FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance);
foreach (var field in fields)
{
try
{
//复制字段值
field.SetValue(retval, DeepCopyByReflection(field.GetValue(obj)));
}
catch { }
}
return (T)retval;
}

控制台输出结果:

1
2
3
4
A:Name:张三,Gender:男
B:Name:张三,Gender:男
A:Name:张三,Gender:男
B:Name:李四,Gender:女

String 类型

到了这里产生一个疑问,String同样为引用类型,但在平时使用过程中并没有出现上述引用类型浅拷贝的问题

1
2
3
4
5
6
7
8
9
10
11
public static void Main(string[] args)
{
string a = "123", b = a;
Console.WriteLine($"a:{a}");
Console.WriteLine($"b:{b}");

b = "456";
Console.WriteLine($"a:{a}");
Console.WriteLine($"b:{b}");
Console.ReadKey();
}

控制台输出结果:

1
2
3
4
a:123
b:123
a:123
b:456

微软官方文档给出的解释

字符串是不可变的 ,即:字符串对象在创建后,尽管从语法上看似乎可以更改其内容,但事实上并不可行。 例如,在上述代码b = “456”中,编译器实际上会创建一个新的字符串对象来保存新的字符序列,且该新对象将赋给 b。 已为 b 分配的内存可用于垃圾回收。


C#中的浅拷贝与深拷贝
https://wangyuangen.github.io/2020/10/11/DeepClone/
作者
Yuangen Wang
发布于
2020年10月11日
许可协议