Click here to Skip to main content
15,867,330 members
Articles / Web Development / ASP.NET / ASP.NET4.0

Implementing Deep Cloning using Reflection

Rate me:
Please Sign up or sign in to vote.
4.33/5 (7 votes)
20 Sep 2016CPOL 33.2K   12   6
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionUsing Statics Pin
Nash Michael22-Sep-16 8:53
professionalNash Michael22-Sep-16 8:53 
AnswerRe: Using Statics Pin
Alen Toma22-Sep-16 9:25
Alen Toma22-Sep-16 9:25 
QuestionArray cloning Pin
Philippe Mori21-Sep-16 6:16
Philippe Mori21-Sep-16 6:16 
AnswerRe: Array cloning Pin
Alen Toma21-Sep-16 6:28
Alen Toma21-Sep-16 6:28 
GeneralRe: Array cloning Pin
Philippe Mori21-Sep-16 8:54
Philippe Mori21-Sep-16 8:54 
While it works for string, it won't generally works for reference type. So an array of string is a bad example. Try an array of objects instead.
Philippe Mori

GeneralRe: Array cloning Pin
Alen Toma21-Sep-16 8:55
Alen Toma21-Sep-16 8:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.