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
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();
var customList = typeof(List<>).MakeGenericType
((properties[properties.Length - 1]).PropertyType);
tObject = (IList)Activator.CreateInstance(customList);
var list = (IList)tObject;
foreach (var item in ((IList)_desireObjectToBeCloned))
{
if (item == null)
continue;
var value = new FastDeepCloner(item).DeepClone();
list?.Add(value);
}
}
else
{
if (_primaryType == typeof(string))
return (_desireObjectToBeCloned as string)?.Clone();
tObject = FormatterServices.GetUninitializedObject(_primaryType);
var fields = _desireObjectToBeCloned.GetType().GetFields(Binding);
foreach (var property in fields)
{
if (property.IsInitOnly) 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:
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(); // 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