浅拷贝
值类型
值类型的拷贝是将源拷贝对象的值赋予被拷贝对象,在修改被拷贝对象的值之后并不会影响源拷贝对象的值
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 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 = 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()}"); }
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(); }
|
控制台输出结果:
微软官方文档给出的解释
字符串是不可变的 ,即:字符串对象在创建后,尽管从语法上看似乎可以更改其内容,但事实上并不可行。 例如,在上述代码b = “456”中,编译器实际上会创建一个新的字符串对象来保存新的字符序列,且该新对象将赋给 b。 已为 b 分配的内存可用于垃圾回收。