65.9K
CodeProject 正在变化。 阅读更多。
Home

使用反射实现深度克隆

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (7投票s)

2016年9月21日

CPOL
viewsIcon

35601

如何使用反射实现深度克隆。

引言

在我的工作中,我经常需要处理包含复杂计算和验证的大对象。我需要能够修改对象的属性,并在需要时撤销这些修改。

最简单的方法是使用DeepClone(深度克隆)对象。问题是,市面上有很多库,它们要么难以定制以满足我的需求,要么速度太慢,所以我决定自己编写一个。

更新

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
        }
}

以下是使用此类的几种方法

  1. Employee employee = new FastDeepCloner.FastDeepCloner(original).Clone<Employee>();
  2. List<Employee> employee = new FastDeepCloner.FastDeepCloner(original).Clone<List<Employee>>();
  3. object employee = new FastDeepCloner.FastDeepCloner(original).Clone(); // 用于 System.Object

关注点

我期待您的想法。我可能会通过忽略属性并使其更快来使其更加高级。

历史

  • 2016-09-20:版本 1.0.1:发布到 NuGet
© . All rights reserved.