Mama Tate
Mama Tate

Reputation: 283

Copying a template object to create a new one from it

I don't exactly know how to approach this type of problem that i have.

private Dictionary<int, Tire> m_vehicleTireSelected = new Dictionary<int, Tire>()
{
    {0, new TireCasual()
    { 
        Name = "Monster Tire", 
        Position = new Vector3(-0.94f, -1.09f) 
    }},
    {1, new TireMonster()
    { 
        Name = "Casual Tire", 
        Position = new Vector3(1.05f, -1.09f) 
    }}
};


public void ChangeTire(int tireIndex, int tireKey)
{
    m_bus.ChangeTire(tireIndex, m_vehicleTireSelected[tireKey]);
}

So i want to use for instance Dictionary here in order to store some tire template objects and later on to change them with new ones. The problem here is when i assign the tire from the dictionary it's still the same tire because it is a reference type variable, but eventually i want it a COPY. Can somebody help me out and maybe throw an idea how i can approach this scenario? I should also mention that this is performance critical part.

Upvotes: 2

Views: 1060

Answers (1)

Etienne Maheu
Etienne Maheu

Reputation: 3255

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.

Upvotes: 3

Related Questions