Cloning objects the quick and dirty way

Working in .NET, a lot of the objects I use implement ISerializable – very handy when it comes to throwing them around using .NET Remoting and the like. In my slightly more evil (or is it lazy?) moments, I tend to “re-purpose” this for a lightweight clone mechanism…

It’s quite common when writing test code to be able to do a deep copy of an object – maybe to keep a copy of its state before performing an operation to then compare it again later, or maybe to “subclass” an existing object, overriding a given property with something else. Even preventing certain properties going over the wire or to disk because they aren’t required, all without the original object knowing anything of the devious things going on.

The problem is, there’s no generic deep copy facility available in .NET (correctly so), and I generally don’t want to have to implement IClonable on everything as well. What to do?

Assume I have a couple of little classes, Outer, and Inner, where Outer has a property of type Inner:

    [Serializable]
    class Outer : ISerializable
    {
        public Inner Inner { get; set; }

        public Outer() { }

        public Outer(SerializationInfo info, StreamingContext context)
        {
            Inner = (Inner)info.GetValue(“Inner”, typeof(Inner));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue(“Inner”, Inner);
        }
    }

    [Serializable]
    class Inner : ISerializable
    {
        public Inner() { }

        public Inner(SerializationInfo info, StreamingContext context) { }

        public void GetObjectData(SerializationInfo info, StreamingContext context) { }
    }

My first solution was along these lines – very compact, and seemed to work:

    class ClonedOuter : Outer
    {
        public ClonedOuter(Outer o)
            : base(GetSerializationInfo(o), new StreamingContext ())
        { }

        private static SerializationInfo GetSerializationInfo(Outer o)
        {
            SerializationInfo info = new SerializationInfo(typeof(Outer), new FormatterConverter());
            o.GetObjectData(info, new StreamingContext());
            return info;
        }
    }

The theory went that Outer would serialize its state to the SerializationInfo I provided in GetSerializationInfo, and that would then get passed to the deserialization constructor of Outer via the base(…) call, giving a nicely duplicated object. And it did! But then I hit a problem:

            Outer clone1 = new ClonedOuter(original);
            Console.WriteLine(“clone1.Inner == original.Inner: ” + (clone1.Inner == original.Inner)); // Prints true!

Whilst the Outer object was being cloned, the Inner one was not – it was simply the same object. The reason for this is hinted at in the documentation: “Objects are reconstructed from the inside out.” The GetObjectData of Inner wasn’t even being called, let alone being used.

Back to the drawing board. This time, a slightly more “authorized” use of serialization:

        public static Outer Clone(Outer o)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, o);
                ms.Seek(0, SeekOrigin.Begin);
                return (Outer)bf.Deserialize(ms);
            }
        }

Here we actually force the object graph to be serialized to a MemoryStream (basically a wrapper around a byte[]), and the results are as expected. Much better. But it’s not perfect: I quite like the ability to have a “copy constructor” of the form “new ObjectyThing(ObjectyThing source)”, and the static Clone method doesn’t give me the opportunity to subclass the cloned copy.

So then you combine both approaches:

    class ClonedOuter2 : Outer
    {
        public ClonedOuter2(Outer o)
            : base(GetSerializationInfo(Clone(o)), new StreamingContext())
        { }

        public static Outer Clone(Outer o)
        {
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream())
            {
                bf.Serialize(ms, o);
                ms.Seek(0, SeekOrigin.Begin);
                return (Outer)bf.Deserialize(ms);
            }
        }

        private static SerializationInfo GetSerializationInfo(Outer o)
        {
            SerializationInfo info = new SerializationInfo(typeof(Outer), new FormatterConverter());
            o.GetObjectData(info, new StreamingContext());
            return info;
        }
    }

I dread to think of the efficiency of this… 🙂