This is the second in a series of posts about performance in the .NET ecosystem. On the first post, that you can find here, I talked about object instantiation. This time, it’s object cloning.
You should be aware that there are two types of cloning:
- Deep cloning: clones the root object and each reference its properties points to
- Shallow cloning: clones the root object but each reference any of its properties points to is kept, that is, is also pointed to, not cloned
Microsoft defines one interface, ICloneable, which is implemented by a couple of classes, but doesn’t really say much about the type of cloning that it does. In fact, Microsoft actually advises against using it.
Sometimes we definitely need to make a copy of an existing object. This includes creating a new instance and then populating it with the values (or copies of) for the properties of the initial class. We have a few options:
- Using the built-in MemberwiseClone method
- Implementing our own cloning strategy
- Using an object-to-object mapper
- Serializing and deserializing
Memberwise Shallow Clone
The Object class defines a MemberwiseClone method that the documentation describes as doing a shallow copy of the current object. Because this is defined for Object, all classes inherit it, and it will make a copy of all fields declared along the class hierarchy. But, alas, being generic also means that it is not particularly suited for any concrete scenario. It uses it uses FormatterServices.GetUninitializedObject to create a “blank” instance of your class and then reflection to iterate through all of the instance fields of your class and makes a bitwise copy of them. Because of the reflection part, it’s probably not the fastest solution.
Here you need to decide if you’re going for a shallow or a deep strategy, and depending on that, things can be more or less complicated. In general, a shallow copy is faster and simpler to implement as we know exactly what properties to copy, and you can even go for a shallow or a deep copy. An option is to use an object mapper.
Using a Mapper
Using a mapper such as Automapper can prove very useful as it can do lots of things out-of-the-box for you and still leave you space to handle any more complex situations. There are many out there, but Automapper is probably the most popular but there are alternatives:
So, the idea is, you construct the target instance (the copy) yourself and then you use the mapper to copy from the current instance into it. A decent mapper can use conventions to copy without requiring any configuration, but it will still require some reflection magic.
Serializing and Deserializing
This one, like using a mapper, also requires a third-party library. There are plenty out there, even included in the .NET framework, that can do this. This may seem like an overkill if you’re just trying to get a clone of your current object, but it works, and it is usually one of the easiest ways to get a deep copy. This is usually used for transmitting an object’s state over the wire, in any format that can be binary or text-based, and it can go back as well, translating this state back into a class. Usually also not the fastest approach, especially for complex object graphs.
Performance-wise, you’re probably better off by rolling out your cloning method, which will probably be unique for each class. This avoids reflection and serialization (which also uses reflection). Just make it clear to everyone that you’re implementing a shallow or a deep copy.