Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Implementing Deep Cloning using Reflection

0.00/5 (No votes)
20 Sep 2016 1  
How to implement deep cloning using Reflection

Introduction

In my line of work, I have many big objects that need to be able to handle advanced calculations and validation. I needed to be able to make changes to objects' properties and then revoke the changes if needed.

The easiest way was to DeepClone the object. The problem was that there are too many libraries out there that were too hard to customize to my requirements and were too slow, so I decided to make my own.

Update

Using the Code

This is the class that will perform the DeepClone, and also the object does not need to be [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
        }
}

Ways to use this class are listed below:

  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(); // for System.Object

Points of Interest

I am waiting for your thoughts about this. I may make it more advanced by making it ignore properties and also by making it faster.

History

  • 2016-09-20: Version 1.0.1: released on NuGet

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here