0

I am trying to implement a deep clone feature and already asked some question:

I am modifing the following code: https://stackoverflow.com/a/11308879/2598770

And currently I am facing the problem, that my original list also gets modified with "copied" objects instead of only the clone object, as recommended by the 2nd earlier question (Array, List, IEnumerable, CustomList class cast to one and iterate threw them). I have change the following code:

//Code directly from source
var cloneObject = CloneMethod.Invoke(originalObject, null);
if (typeToReflect.IsArray)
{
    var arrayType = typeToReflect.GetElementType();
    if (IsPrimitive(arrayType) == false)
    {
        Array clonedArray = (Array)cloneObject;
        clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), visited), indices));
    }
}

To a version that also handles, IList and not just arrays:

var cloneObject = CloneMethod.Invoke(originalObject, null);
if (cloneObject is IList)
{
    if (typeToReflect.GetGenericArguments().Any())
    {
        var arrayType = typeToReflect.GenericTypeArguments[0];
        if (IsPrimitive(arrayType) == false)
        {
            var clonedArray = (IList)cloneObject;
            if (clonedArray.IsReadOnly == false)
                for (var i = 0; i < clonedArray.Count; i++)
                {
                    var originalListEntry = clonedArray[i];
                    var clonedListEntry = InternalCopy(originalListEntry, visited);
                    clonedArray[i] = clonedListEntry;
                }
        }
    }
}

but on the line clonedArray[i] = clonedListEntry;, it doesnt just change the clonedArray but also the originalObject.

How can I prevent this, so that the clonedListEntry only gets set on the clonedArray?

Community
  • 1
  • 1
Rand Random
  • 7,300
  • 10
  • 40
  • 88

1 Answers1

1

You need to create a new container instead of just getting a reference to cloneObject. For example:

var existingList = (IList)cloneObject;
var clonedArray = Array.CreateInstance(arrayType, existingList.Count);

You can then go on to populate clonedArray. In the existing code the clonedArray is just a reference to your original list (it's not even guaranteed to be an array!), so any changes made to it get reflected to the original as well.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • But shouldnt this already be done by the line `var cloneObject = CloneMethod.Invoke(originalObject, null);` ? – Rand Random Jan 08 '14 at 11:32
  • @RandRandom: I honestly don't know because I don't see the implementation anywhere. But isn't the problem description enough proof that it doesn't clone as it appears to be doing? – Jon Jan 08 '14 at 11:35
  • Here you have a direct link to the source code https://raw.github.com/Burtsev-Alexey/net-object-deep-copy/master/ObjectExtensions.cs (its from the post: http://stackoverflow.com/a/11308879/2598770), The CloneMethod does the following `private static readonly MethodInfo CloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);` – Rand Random Jan 08 '14 at 11:37
  • @RandRandom: `MemberwiseClone` is [documented](http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone%28v=vs.110%29.aspx) to make a *shallow* copy of its input: "If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object." – Jon Jan 08 '14 at 11:40