Considering that performance is a major issue in your code, I would refrain from using automated deep cloning techniques. Instead, I recommend using the most efficient memory cloning technique available in C#, Object.MemberwiseClone()
(see the MSDN page here). This method is pretty much a wrapper around the C memcpy()
API which directly copies the referenced block of memory and makes this whole process lightning fast. The only caveat of this technique is that it creates a shallow copy. That is, references types in your object will still points to the same instance. To handle this properly, you need a little bit of extra work.
public interface IDeepCloneable<T>
{
T DeepClone();
}
class Foo : IDeepCloneable<Foo>
{
public Foo DeepClone()
{
// The simplest usecase
return (Foo)this.MemberwiseClone();
}
}
class Bar : IDeepCloneable<Bar>
{
private Foo _foo;
private List<Foo> _lists;
private List<int> _valueTypedLists;
public Bar DeepClone()
{
var clone = (Bar)this.MemberwiseClone();
// This makes sure that deeper references are also cloned.
clone._foo = _foo.DeepClone();
// Though you still need to manually clone types that you do not own like
// lists but you can also turn this into an extension method if you want.
clone._lists = _lists.Select(f => f.DeepClone()).ToList();
// And you can simply call the ToList/ToArray method for lists/arrays
// of value type entities.
clone._valueTypedLists = _valueTypedLists.ToList();
return clone;
}
}
I did some benchmarks to compare this technique and the binary serializer technique proposed in the comments above. Here are the results:
1 000 000 objects composed of 4 ints
Binary Serializer : 10.361799 seconds.
MemberwiseClone : 0.080879 seconds. (128x gain)
1 000 000 objects composed of 4 Lists<int> with 4 items (16 ints + 4 List)
Binary Serializer : 47.288164 seconds.
MemberwiseClone : 0.517383 seconds. (91x gain)
PS: You might have noticed that I use my own interface instead of System.ICloneable. That is because the built-in interface dates of the .NET 2.0 era when generics where not available. It also have a major caveat as it does not properly state its intent. Basically, there is no way to know what will come out of the Clone method. Is it a shallow copy, a deep copy, is it even of the same type? There is no way to make sure. This is why I recommend implementing your own IDeepCloneable and IShallowCloneable interfaces.