I have to implement generic extention deepclone method which can be used with any reference type instance to get its deep copy. I implement it as the following
static class ClassCopy
{
static public T DeepClone<T> (this T instance)
{
if (instance == null) return null;
var type = instance.GetType();
T copy;
var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
var fields = type.GetFields(flags);
// If type is serializable - create instance copy using BinaryFormatter
if (type.IsSerializable)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, instance);
stream.Position = 0;
copy = (T) formatter.Deserialize(stream);
}
// Copy all fiels which are not marked as serializable
foreach (var field in fields)
{
if (!field.IsNotSerialized) continue;
var value = field.GetValue(instance);
//Recursion!!!
//for each embedded object also create deep copy
value = value != null ? value.DeepClone() : value;
field.SetValue(copy, value);
}
}
else
{
// If type is not serializable - create instance copy using Activator
//(if there is default constructor)
// or FormatterServices ( if there is no constractor)
copy = CreateInstance<T>(type);
foreach (var field in fields)
{
var value = field.GetValue(instance);
//Recursion!!!
value = value != null ? value.DeepClone() : value;
field.SetValue(copy, value);
}
}
//Copy all properties
//In order to copy all backing fields for auto-implemented properties
var properties = type.GetProperties(flags|BindingFlags.SetProperty);
foreach (var property in properties)
{
if (property.CanWrite)
{
var value = property.GetValue(instance);
//Recursion!!!
value = value != null ? value.DeepClone() : null;
property.SetValue(copy, value);
}
}
return copy;
}
private static T CreateInstance<T>(Type t) where T: class
{
T instance;
var constructor = t.GetConstructor(Type.EmptyTypes);
if (constructor != null)
{
instance = Activator.CreateInstance(t) as T;
return instance;
}
instance = FormatterServices.GetUninitializedObject(t) as T;
return instance;
}
}
It works well. But if object to be cloned and its reference type fields have mutual references this code leads to infinite loops. e.g.
private static void Main(string[] args)
{
var parent = new Parent();
parent.Child = new Child();
parent.Child.Parent = parent;
//Infinite Loop!!!
var parent1 = parent.DeepClone();
}
class Parent
{
public Child Child { get; set; }
}
class Child
{
public Parent Parent { get; set; }
}
Does anyone has any idea how to implement this task? It should be implemented literally and no variations are allowed (it's a practicum). Thanks a lot for any tips!
An old trick for deep-cloning objects, is to serialize and de-serialize them, thereby creating new instances.
public T deepClone<T>(T toClone) where T : class
{
string tmp = JsonConvert.SerializeObject(toClone);
return JsonConvert.DeserializeObject<T>(tmp);
}
I've worked extensively with Newtonsoft.Json
and it has a built in solution to your problem. By default, it detects if an object has already be serialized and throws an exception. However, you can configure it to serialize references to object for getting around circular references. Instead of serializing objects in-line, it serializes a reference to that object and guarantees that every reference to an object is only serialized once.
Further, the default is to only serialize public fields/properties. There is an additional setting for serializing the private fields too.
public T deepClone<T>(T toClone) where T : class
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
DefaultContractResolver dcr = new DefaultContractResolver();
dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
settings.ContractResolver = dcr;
string tmp = JsonConvert.SerializeObject(toClone, settings);
return JsonConvert.DeserializeObject<T>(tmp);
}
So you could either "cheat" and use code like this or copy how it works to implement a clone that preserves references. The example you gave of parent / child is just 1 way cloning is difficult. 1 to many is another.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With