C# 浅拷贝和深拷贝的实现

拷贝(复制)为对象创建副本,即将对象中的所有字段复制到新的对象(副本中)。拷贝有两种:浅拷贝和深拷贝,微软建议用类型继承ICloneable接口的方式明确该类型是可以被拷贝的,ICloneable接口只提供了一个Clone方法,需要根据需要在Clone方法内实现浅拷贝或深拷贝。

1、浅拷贝:把源对象中的值类型字段的引用类型字段的引用复制到副本中。在源对象(副本)中,修改值类型字段的值不会影响到副本(源对象),而修改引用类型字段的值会影响到副本(源对象)。

注意string类型除外,虽然string类型是引用类型,但是由于该引用类型的特殊性,在浅拷贝过程,副本中会创建新的字符串并把对应的值复制过来,字符串应被看成值类型。

浅拷贝声明代码,使用Object.MeberwiseClone方法进行浅拷贝:

 1 class Employee : ICloneable 2  { 3 public string ID { get; set; } 4 public int Age { get; set; } 5 public Department DepartmentName { get; set; } 6  7 //实现ICloneable接口的Clone方法 8 public object Clone() 9  {10 return this.MemberwiseClone();//浅拷贝11  }12  }13 class Department14  {15 public string DepartmentName { get; set; }16 public Department(string value)17  {18 DepartmentName = value;19  }20 public override string ToString()21  {22 return DepartmentName.ToString();23  }24 }

调用浅拷贝代码:

 1 Employee emp1 = new Employee() 2  { 3 ID = "NO1", 4 Age = 20, 5 DepartmentName = new Department("Technology") 6  }; 7 Employee emp2 = emp1.Clone() as Employee;//浅拷贝 8  9 Console.WriteLine("-------初始化赋值------");10 Console.WriteLine(string.Format("[emp1] id:{0}\tage:{1}\tdepartment:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));11 Console.WriteLine(string.Format("[emp2] id:{0}\tage:{1}\tdepartment:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));12 13 Console.WriteLine("\n-------改变emp1的值-------");14 emp1.ID = "NO2";15 emp1.Age = 22;16 emp1.DepartmentName.DepartmentName = "sales";17 Console.WriteLine(string.Format("[emp1] id:{0}\tage:{1}\tdepartment:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));18 Console.WriteLine(string.Format("[emp2] id:{0}\tage:{1}\tdepartment:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));19 20 Console.WriteLine("\n-------改变emp2的值-------");21 emp2.ID = "NO3";22 emp2.Age = 24;23 emp2.DepartmentName.DepartmentName = "personnel";24 Console.WriteLine(string.Format("[emp1] id:{0}\tage:{1}\tdepartment:{2}", emp1.ID, emp1.Age, emp1.DepartmentName));25 Console.WriteLine(string.Format("[emp2] id:{0}\tage:{1}\tdepartment:{2}", emp2.ID, emp2.Age, emp2.DepartmentName));

运行结果:

-------初始化赋值------[emp1] id:NO1 age:20 department:Technology[emp2] id:NO1 age:20 department:Technology-------改变emp1的值-------[emp1] id:NO2 age:22 department:sales[emp2] id:NO1 age:20 department:sales-------改变emp2的值-------[emp1] id:NO2 age:22 department:personnel[emp2] id:NO3 age:24 department:personnel

从结果可以看出,Age是值类型,ID是string类型这里被当做值类型处理,所以ID和Age修改了对另一个对象没有影响;Department属性是引用类型,浅拷贝emp1和emp2引用的是同一个Department对象,其中一个修改了DepartmentName的值会影响另一个。

 

2、深拷贝:把源对象的值类型字段和引用类型字段,重新创建并赋值。在源对象(副本)中,修改值类型字段的值或者引用类型字段的值都不会影响到副本(源对象)。

建议使用序列化的形式来进行深拷贝

深拷贝代码:

 [Serializable]//标记可序列化 class Employee : ICloneable { public string ID { get; set; } public int Age { get; set; } public Department DepartmentName { get; set; } //实现ICloneable接口的Clone方法 public object Clone() { using (MemoryStream stream = new MemoryStream()) { BinaryFormatter formatter = new BinaryFormatter(); formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(stream) as Employee; } } } [Serializable]//标记可序列化 class Department { public string DepartmentName { get; set; } public Department(string value) { DepartmentName = value; } public override string ToString() { return DepartmentName.ToString(); } }

跟上面调用浅拷贝代码一样调用深拷贝,运行结果:

-------初始化赋值------[emp1] id:NO1 age:20 department:Technology[emp2] id:NO1 age:20 department:Technology-------改变emp1的值-------[emp1] id:NO2 age:22 department:sales[emp2] id:NO1 age:20 department:Technology-------改变emp2的值-------[emp1] id:NO2 age:22 department:sales[emp2] id:NO3 age:24 department:personnel

拷贝以后,无论是修改值类型还是引用类型,都对另一个对象没有影响。

3、要同时实现深拷贝和浅拷贝,可以在Clone方法外,额外实现两个方法,声明为DeepClone和Shallow:

 1 [Serializable]//标记可序列化 2 class Employee : ICloneable 3  { 4 public string ID { get; set; } 5 public int Age { get; set; } 6 public Department DepartmentName { get; set; } 7  8 //实现ICloneable接口的Clone方法 9 public object Clone()10  {11 return this.MemberwiseClone();12  }13 14 //深拷贝15 public Employee DeepClone()16  {17 using (MemoryStream stream = new MemoryStream())18  {19 BinaryFormatter formatter = new BinaryFormatter();20 formatter.Serialize(stream, this);21 stream.Seek(0, SeekOrigin.Begin);22 return formatter.Deserialize(stream) as Employee;23  }24  }25 26 //浅拷贝27 public Employee Shallow()28  {29 return this.Clone() as Employee;30  }31 }

 

参考:《编写高质量代码改善C#程序的157个建议》陆敏技

相关文章