VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > temp > C#教程 >
  • C#对象属性浅拷贝和深拷贝

本文为作者原创,转载请注明出处:https://www.cnblogs.com/zhaoqingqing/p/14800759.html



对象属性和字段拷贝的几种方式#

微软提供了浅拷贝

  • 对于值类型,修改拷贝的值不会影响源对象
  • 对于引用类型,修改拷贝后的值会影响源对象,但string特殊,它会拷贝一个副本,互相不会影响

自己实现深拷贝,我了解到的有这几种方法

  1. 硬核编码,每一个属性和字段都写一遍赋值,这种方法运行速度最快
  2. 通过反射,最常见的方法,但每次都需要反射
  3. 通过序列化,需要给类加上[Serializable]标签
  4. C# 快速高效率复制对象另一种方式 表达式树

测试例子#

例子代码在文章未尾,这里先展示测试结果。

最开始创建对象的字段值为: Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士

1.原始值变化后,使用深浅两种拷贝的结果


	
Copy
//原始值:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士, //修改原始值的Id和Name,Skin字段之后,输出如下: //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:隐刃, //浅拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:隐刃, //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士

2.修改浅拷贝的值,再打印看看结果


	
Copy
//输出:修改浅拷贝的Id,Name,Prof,Skin,输出如下: //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:隐刃, //浅拷贝:Id:1008,Name:李白,Hp:3449,Prof:刺客,Skin:凤求凰, //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士

㳀拷贝#

MemberwiseClone#

Object.MemberwiseClone函数定义:


	
Copy
/// <summary> /// 创建当前 <see cref="T:System.Object" /> 的浅表副本。 /// </summary> /// <returns> /// 当前 <see cref="T:System.Object" /> 的浅表副本。 /// </returns> protected extern object MemberwiseClone();

结论#

MemberwiseClone理论上满足常见的需求,包括string这种特殊类型,拷贝后的副本与原始值是断开联系,修改不会相互影响。

反射对于List、Hashtable等复杂结构需要特殊处理

例子#


	
Copy
[Serializable] class XEngine : ICloneable { public object Clone() { return this.MemberwiseClone(); } }

深拷贝#

比较常见的就是通过反射对所有字段和属性进行赋值,还可以通过序列化也是可以对所有字段和属性赋值。

序列化#


	
Copy
public XEngine DeepClone() { using (Stream objectStream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(objectStream, this); objectStream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(objectStream) as XEngine; } }

反射拷贝#

反射所有的属性和字段,进行赋值,但对于hashtable和list等复杂结构是不好处理的。


	
Copy
public void ReflectClone(object from, object to) { if (from == null || to == null) { Debug.LogError($"拷贝失败,from is null:{from == null},to is null:{to == null}"); return; } var fromType = from.GetType(); var toType = to.GetType(); //拷贝属性 var properties = fromType.GetProperties(); foreach (PropertyInfo prop in properties) { var toProp = toType.GetProperty(prop.Name); if (toProp != null) { var val = prop.GetValue(from); if (prop.PropertyType == toProp.PropertyType) { toProp.SetValue(to, val, null); } else if (prop.PropertyType.ToString().IndexOf("List") >= 0 || prop.PropertyType.ToString().IndexOf("Hashtable") >= 0) { Debug.LogError($"属性:{prop.Name},不支持List和Hashtable的拷贝,请使用序列化"); } } } //拷贝字段 var fields = fromType.GetFields(); foreach (FieldInfo field in fields) { var toField = toType.GetField(field.Name); if (toField != null) { var val = field.GetValue(from); if (field.FieldType == toField.FieldType) { toField.SetValue(to, val); } else if (field.FieldType.ToString().IndexOf("List") >= 0 || field.FieldType.ToString().IndexOf("Hashtable") >= 0) { Debug.LogError($"字段:{field.Name},不支持List和Hashtable的拷贝,请使用序列化"); } } } }

在Unity中的例子#

unity引擎版本:2019.3.7f1,完整代码如下:


	
Copy
using System; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using UnityEngine; using Object = System.Object; /// <summary> /// Author:qingqing.zhao (569032731@qq.com) /// Date:2021/5/18 10:54 /// Desc:在Unity中测试几种对象拷贝的方法 /// 1.微软提供的浅拷贝 /// 2.序列化 /// 3.反射拷贝 ///结论:int,bool等值类型和string浅拷贝之后修改原始值不会影响clone值,但引用类型会影响 /// </summary> public class CloneDemo : MonoBehaviour { private void Start() { #region 例子1 //测试修改一个只有基础数据结构的类,结论:int和string浅拷贝之后修改源始值不会影响clone值 XCharacter role = new XCharacter() {Id = 1001, Name = "亚瑟", Hp = 3449, Prof = "战士", Skin = new XSkin() {Name = "死亡骑士"}}; Debug.Log($"原始值:{role.ToString()}"); XCharacter simpleClone = role.Clone() as XCharacter; XCharacter deepClone = role.DeepClone(); role.Id = 1005; role.Name = "兰陵王"; role.Prof = "刺客"; role.Skin.Name = "影刃"; Debug.Log($"修改原始值,原始值:{role.ToString()},浅拷贝:{simpleClone.ToString()},深拷贝:{deepClone.ToString()}"); //输出:修改原始值, //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃, //浅拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:影刃, //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士 simpleClone.Id = 1008; simpleClone.Prof = "刺客"; simpleClone.Name = "李白"; Debug.Log($"修改浅拷贝的值,原始值:{role.ToString()},浅拷贝:{simpleClone.ToString()},深拷贝:{deepClone.ToString()}"); //输出:修改浅拷贝的值, //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃, //浅拷贝:Id:1008,Name:李白,Hp:3449,Prof:刺客,Skin:影刃, //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士 #endregion #region 通过反射拷贝 XCharacter reflectClone = new XCharacter(); ReflectClone(role, reflectClone); Debug.Log($"反射拷贝,原始值:{role.ToString()},反射拷贝:{reflectClone.ToString()}"); //输出:反射拷贝, //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃, //反射拷贝:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃 #endregion } } [Serializable] class XCharacter : ICloneable { public int Id { get; set; } public string Name { get; set; } public int Hp; public string Prof; public XSkin Skin { get; set; } public override string ToString() { return $"Id:{Id},Name:{Name},Hp:{Hp},Prof:{Prof},Skin:{Skin?.ToString()}"; } public object Clone() { return this.MemberwiseClone(); } public XCharacter DeepClone() { using (Stream objectStream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(objectStream, this); objectStream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(objectStream) as XCharacter; } } } [Serializable] class XSkin { public string Name { get; set; } public override string ToString() { return this.Name; } }
作者:赵青青   一名在【网易游戏】做游戏开发的程序员,擅长Unity3D,游戏开发,.NET等领域。

相关教程