经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 游戏设计 » 查看文章
C#对象属性浅拷贝和深拷贝
来源:cnblogs  作者:赵青青  时间:2021/5/24 10:35:59  对本文有异议

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

微软提供了浅拷贝

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

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

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

测试例子

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

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

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

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

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

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

?拷贝

MemberwiseClone

Object.MemberwiseClone函数定义:

  1. /// <summary>
  2. /// 创建当前 <see cref="T:System.Object" /> 的浅表副本。
  3. /// </summary>
  4. /// <returns>
  5. /// 当前 <see cref="T:System.Object" /> 的浅表副本。
  6. /// </returns>
  7. protected extern object MemberwiseClone();

结论

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

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

例子

  1. [Serializable]
  2. class XEngine : ICloneable
  3. {
  4. public object Clone()
  5. {
  6. return this.MemberwiseClone();
  7. }
  8. }

深拷贝

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

序列化

  1. public XEngine DeepClone()
  2. {
  3. using (Stream objectStream = new MemoryStream())
  4. {
  5. IFormatter formatter = new BinaryFormatter();
  6. formatter.Serialize(objectStream, this);
  7. objectStream.Seek(0, SeekOrigin.Begin);
  8. return formatter.Deserialize(objectStream) as XEngine;
  9. }
  10. }

反射拷贝

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

  1. public void ReflectClone(object from, object to)
  2. {
  3. if (from == null || to == null)
  4. {
  5. Debug.LogError($"拷贝失败,from is null:{from == null},to is null:{to == null}");
  6. return;
  7. }
  8. var fromType = from.GetType();
  9. var toType = to.GetType();
  10. //拷贝属性
  11. var properties = fromType.GetProperties();
  12. foreach (PropertyInfo prop in properties)
  13. {
  14. var toProp = toType.GetProperty(prop.Name);
  15. if (toProp != null)
  16. {
  17. var val = prop.GetValue(from);
  18. if (prop.PropertyType == toProp.PropertyType)
  19. {
  20. toProp.SetValue(to, val, null);
  21. }
  22. else if (prop.PropertyType.ToString().IndexOf("List") >= 0 || prop.PropertyType.ToString().IndexOf("Hashtable") >= 0)
  23. {
  24. Debug.LogError($"属性:{prop.Name},不支持List和Hashtable的拷贝,请使用序列化");
  25. }
  26. }
  27. }
  28. //拷贝字段
  29. var fields = fromType.GetFields();
  30. foreach (FieldInfo field in fields)
  31. {
  32. var toField = toType.GetField(field.Name);
  33. if (toField != null)
  34. {
  35. var val = field.GetValue(from);
  36. if (field.FieldType == toField.FieldType)
  37. {
  38. toField.SetValue(to, val);
  39. }
  40. else if (field.FieldType.ToString().IndexOf("List") >= 0 || field.FieldType.ToString().IndexOf("Hashtable") >= 0)
  41. {
  42. Debug.LogError($"字段:{field.Name},不支持List和Hashtable的拷贝,请使用序列化");
  43. }
  44. }
  45. }
  46. }

在Unity中的例子

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

  1. using System;
  2. using System.IO;
  3. using System.Reflection;
  4. using System.Runtime.Serialization;
  5. using System.Runtime.Serialization.Formatters.Binary;
  6. using UnityEngine;
  7. using Object = System.Object;
  8. /// <summary>
  9. /// Author:qingqing.zhao (569032731@qq.com)
  10. /// Date:2021/5/18 10:54
  11. /// Desc:在Unity中测试几种对象拷贝的方法
  12. /// 1.微软提供的浅拷贝
  13. /// 2.序列化
  14. /// 3.反射拷贝
  15. ///结论:int,bool等值类型和string浅拷贝之后修改原始值不会影响clone值,但引用类型会影响
  16. /// </summary>
  17. public class CloneDemo : MonoBehaviour
  18. {
  19. private void Start()
  20. {
  21. #region 例子1
  22. //测试修改一个只有基础数据结构的类,结论:int和string浅拷贝之后修改源始值不会影响clone值
  23. XCharacter role = new XCharacter() {Id = 1001, Name = "亚瑟", Hp = 3449, Prof = "战士", Skin = new XSkin() {Name = "死亡骑士"}};
  24. Debug.Log($"原始值:{role.ToString()}");
  25. XCharacter simpleClone = role.Clone() as XCharacter;
  26. XCharacter deepClone = role.DeepClone();
  27. role.Id = 1005;
  28. role.Name = "兰陵王";
  29. role.Prof = "刺客";
  30. role.Skin.Name = "影刃";
  31. Debug.Log($"修改原始值,原始值:{role.ToString()},浅拷贝:{simpleClone.ToString()},深拷贝:{deepClone.ToString()}");
  32. //输出:修改原始值,
  33. //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃,
  34. //浅拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:影刃,
  35. //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士
  36. simpleClone.Id = 1008;
  37. simpleClone.Prof = "刺客";
  38. simpleClone.Name = "李白";
  39. Debug.Log($"修改浅拷贝的值,原始值:{role.ToString()},浅拷贝:{simpleClone.ToString()},深拷贝:{deepClone.ToString()}");
  40. //输出:修改浅拷贝的值,
  41. //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃,
  42. //浅拷贝:Id:1008,Name:李白,Hp:3449,Prof:刺客,Skin:影刃,
  43. //深拷贝:Id:1001,Name:亚瑟,Hp:3449,Prof:战士,Skin:死亡骑士
  44. #endregion
  45. #region 通过反射拷贝
  46. XCharacter reflectClone = new XCharacter();
  47. ReflectClone(role, reflectClone);
  48. Debug.Log($"反射拷贝,原始值:{role.ToString()},反射拷贝:{reflectClone.ToString()}");
  49. //输出:反射拷贝,
  50. //原始值:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃,
  51. //反射拷贝:Id:1005,Name:兰陵王,Hp:3449,Prof:刺客,Skin:影刃
  52. #endregion
  53. }
  54. }
  55. [Serializable]
  56. class XCharacter : ICloneable
  57. {
  58. public int Id { get; set; }
  59. public string Name { get; set; }
  60. public int Hp;
  61. public string Prof;
  62. public XSkin Skin { get; set; }
  63. public override string ToString()
  64. {
  65. return $"Id:{Id},Name:{Name},Hp:{Hp},Prof:{Prof},Skin:{Skin?.ToString()}";
  66. }
  67. public object Clone()
  68. {
  69. return this.MemberwiseClone();
  70. }
  71. public XCharacter DeepClone()
  72. {
  73. using (Stream objectStream = new MemoryStream())
  74. {
  75. IFormatter formatter = new BinaryFormatter();
  76. formatter.Serialize(objectStream, this);
  77. objectStream.Seek(0, SeekOrigin.Begin);
  78. return formatter.Deserialize(objectStream) as XCharacter;
  79. }
  80. }
  81. }
  82. [Serializable]
  83. class XSkin
  84. {
  85. public string Name { get; set; }
  86. public override string ToString()
  87. {
  88. return this.Name;
  89. }
  90. }

原文链接:http://www.cnblogs.com/zhaoqingqing/p/14800759.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号