Cheap Deep Copy

So I had written a SmtpClientWrapper for System.Net.SmtpClient because the aforementioned class implements no interface making it impossible to perform dependency injection using it (as anyone who has to unit test any sort of email functionality can attest to).  Then I had the idea that during production we may want to mirror email messages sent to a debugging email address so I also made a MirrorSmtpClientDecorator.  Herein lies the problem - System.Net.MailMessage does not implement ICloneable and the MirrorSmtpClientDecorator.Send(MailMessage) either needs a copy or its going adversely affect MailMessage with a side affect (by altering the MailMessage.To MailAddressCollection for a second message sent to the debugging email addresses).  I explored numerous ways to copy MailMessage and eventually I resorted to some brute force extension methods (to ObjectModel.Collection<T>; ShallowCopy and Replace, suck I know) but during my research I discovered some older MemoryStream serialization I refactored and felt its worth sharing.

The problem with this approach is that the class you want to deep copy requires that it be marked with the SerializableAttribute.  I've noticed that a portion of my time is spent writing hackarounds for classes in the .NET BCL and MailMessage is no exception - yup no ICloneable and no SerializableAttribute either.

public class Serializer
{
    public static void Serialize(Stream target, object source)
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(target, source);
    }

    public static T Deserialize<T>(Stream source) where T : class
    {
        var formatter = new BinaryFormatter();
        var obj = (T)formatter.Deserialize(source);
        return obj;
    }
}
 
public class MemorySerializer<T> : IDisposable where T : class
{
    #region Member Variables
    private readonly MemoryStream _ms;
    #endregion

    #region Constructors
    public MemorySerializer(MemoryStream ms)
    {
        _ms = ms;
    }

    public MemorySerializer()
    {
        _ms = new MemoryStream();
    }
    #endregion

    #region Methods
    public MemorySerializer<T> Serialize(T obj)
    {
        Serializer.Serialize(_ms, obj);
        _ms.Position = 0;
        return this;
    }
    public T Deserialize()
    {
        return Serializer.Deserialize<T>(_ms);
    }
    #endregion

    #region Implementation of IDisposable
    public void Dispose()
    {
        if (_ms == null)
            return;
        try
        {
            _ms.Close();
        }
        finally
        {
            _ms.Dispose();
        }
    }
    #endregion
}

public static class ObjectUtility
{
    /// <summary>
    /// Creates a deep copy of an object for any object which
    /// supports binary serialization.
    /// </summary>
    /// <typeparam name="T">Type to serialize/deserialize.</typeparam>
    /// <param name="source">Object to deep copy.</param>
    /// <returns>New instance of the object with all properties and
    /// fields that support serialization copied.</returns>
    /// <remarks>This method isn't as fast as a provided Clone method
    /// but when ICloneable is not implemented on a BCL class its a
    /// pretty close substitute.</remarks>
    public static T DeepCopy<T>(this T source) where T : class
    {
        T ret;
        using (var ms = new MemorySerializer<T>())
        {
            ret = ms.Serialize(source).Deserialize();
        }

       return ret;
    }
}


blog comments powered by Disqus