Wednesday 23 September 2009

Deep cloning without serialization.

A client asked me to write an object cloning implementation today that did not use serialization. Sometimes you cannot mark the types you need to clone as serializable.

I ended up with the code below. In the future, I'll modify it to use IL or something to lose the reflection performance hit.

It's implemented as an extension method so that it should work for any object. Enjoy!



using System;
using System.Collections;
using System.Reflection;

public static class Extensions
{
///
/// Clones the specified object.
///

///
/// The object.
///
public static T Clone(this T obj)
{
var type = obj.GetType();
var clone = Activator.CreateInstance(type);
var fields = clone.GetType().GetFields();
int fieldIndex = 0;
foreach (FieldInfo fieldInfo in type.GetFields())
{
fields[fieldIndex].SetValue(clone, fieldInfo.FieldType.Implements("ICloneable") ? ((ICloneable)fieldInfo.GetValue(obj)).Clone() : fieldInfo.GetValue(obj).Clone());
if (fieldInfo.FieldType.Implements("IEnumerable"))
{
var enumerable = (IEnumerable)fieldInfo.GetValue(obj);
if (fields[fieldIndex].FieldType.Implements("IList"))
{
int i = 0;
IList list = (IList)fields[fieldIndex].GetValue(clone);
foreach (var item in enumerable)
list[i++] = item.Clone();
}
else if (fields[fieldIndex].FieldType.Implements("IDictionary"))
{
IDictionary dictionary = (IDictionary)fields[fieldIndex].GetValue(clone);
foreach (DictionaryEntry entry in enumerable)
dictionary[entry.Key] = entry.Value.Clone();
}
}
fieldIndex++;
}
return (T)clone;
}

///
/// Returns true if the type implements the specified interface, otherwise false.
///

/// The type.
/// Name of the interface.
///
public static bool Implements(this Type type, string interfaceName)
{
return type.GetInterface(interfaceName, true) != null;
}
}