使用反射实现深度克隆
如何使用反射实现深度克隆。
引言
在我的工作中,我经常需要处理包含复杂计算和验证的大对象。我需要能够修改对象的属性,并在需要时撤销这些修改。
最简单的方法是使用DeepClone
(深度克隆)对象。问题是,市面上有很多库,它们要么难以定制以满足我的需求,要么速度太慢,所以我决定自己编写一个。
更新
- Nuget 链接:https://nuget.net.cn/packages/FastDeepCloner/1.0.7
- GitHub 链接(代码):https://github.com/Alenah091/FastDeepCloner-New-
Using the Code
这个类将执行DeepClone
,并且对象不需要是[Serializable]
(可序列化)的。
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.Serialization; namespace FastDeepCloner { #region Privat Properties private const BindingFlags Binding = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy; private readonly Type _primaryType; private readonly object _desireObjectToBeCloned; #endregion #region Contructure public FastDeepCloner(object desireObjectToBeCloned) { if (desireObjectToBeCloned == null) throw new Exception("The desire object to be cloned cant be NULL"); _primaryType = desireObjectToBeCloned.GetType(); _desireObjectToBeCloned = desireObjectToBeCloned; } #endregion #region Privat Method Deep Clone // Clone the object Properties and its children recursively private object DeepClone() { if (_desireObjectToBeCloned == null) return null; if (_primaryType.IsArray) return ((Array)_desireObjectToBeCloned).Clone(); object tObject = _desireObjectToBeCloned as IList; if (tObject != null) { var properties = _primaryType.GetProperties(); // Get the IList Type of the object var customList = typeof(List<>).MakeGenericType ((properties[properties.Length - 1]).PropertyType); tObject = (IList)Activator.CreateInstance(customList); var list = (IList)tObject; // loop throw each object in the list and clone it foreach (var item in ((IList)_desireObjectToBeCloned)) { if (item == null) continue; var value = new FastDeepCloner(item).DeepClone(); list?.Add(value); } } else { // if the item is a string then Clone it and return it directly. if (_primaryType == typeof(string)) return (_desireObjectToBeCloned as string)?.Clone(); // Create an empty object and ignore its construtore. tObject = FormatterServices.GetUninitializedObject(_primaryType); var fields = _desireObjectToBeCloned.GetType().GetFields(Binding); foreach (var property in fields) { if (property.IsInitOnly) // Validate if the property is a writable one. continue; var value = property.GetValue(_desireObjectToBeCloned); if (property.FieldType.IsClass && property.FieldType != typeof(string)) tObject.GetType().GetField(property.Name, Binding)?.SetValue (tObject, new FastDeepCloner(value).DeepClone()); else tObject.GetType().GetField(property.Name, Binding)?.SetValue(tObject, value); } } return tObject; } #endregion #region public Method Clone public object Clone() { return DeepClone(); } public T Clone<T>() { return (T)DeepClone(); } #endregion } }
以下是使用此类的几种方法
Employee employee = new FastDeepCloner.FastDeepCloner(original).Clone<Employee>();
List<Employee> employee = new FastDeepCloner.FastDeepCloner(original).Clone<List<Employee>>();
object employee = new FastDeepCloner.FastDeepCloner(original).Clone(); // 用于 System.Object
关注点
我期待您的想法。我可能会通过忽略属性并使其更快来使其更加高级。
历史
- 2016-09-20:版本 1.0.1:发布到 NuGet