Extending CopyHelper using Generics

2008, Jul 21    

In my last post, I created a method that does the grunt work of copying data from an instance of one class to an instance of another type. I often find myself copying data between the properties of my data layer classes and those of my user interface like this.

</p>

// Copy the data from the customer to the view
view.Address = customer.Address;
view.Country = customer.Country;
view.FirstName = customer.FirstName;
view.LastName = customer.LastName;
view.PostalCode = customer.PostalCode;
view.Province = customer.Province;</code>

The newly created CopyHelper class allows me to shorten that to this.

// Copy the data from the customer to the view (using reflection in .NET 1.x)
CopyHelper.Copy( typeof(ICustomer), customer, typeof(ICustomerView), view );</code>

Today, I want to extend that code using Generics and a fluent interface so that I can write code like this.

// Copy the data from the customer to the view (using reflection and generics in .NET 2.0)
Copier.Copy( customer ).To( view );</code></pre>
</div></p>

Internally, I use my CopyHelper class from my last post. I extend that by creating a generic Copier class. I make the constructor private so that it can only be created as a part of the fluent interface, in this case the static copy method. Using the instance of the Copier class that was returned from that method, you can then copy To or From another class instance.

Here is the code.

public sealed class Copier where T1 : class
{
    #region Private Members

    private readonly T1 _subject;

    #endregion

    #region Public Interface

    /// 
    /// Begin the copying process.
    /// 
    /// The object you are copying from or to</param>
    /// An instance of the Copier class so that you can
    /// continue with the copy to/from in a fluent interface.
    public static Copier Copy( T1 interface1 )
    {
        return new Copier( interface1 );
    }

    #endregion

    #region Construction

    /// 
    /// Private constructor so that it can only be created as a part of a fluent interface.
    /// 
    /// </param>
    private Copier( T1 subject )
    {
        _subject = subject;
    }

    #endregion

    #region Copier Methods

    /// 
    /// Copies properties from the subject to the passed in object.
    /// 
    /// The type of object you are copying into.
    /// The object to copy into.</param>
    /// The modified object that you passed in.
    public T2 To( T2 to ) where T2 : class
    {
        CopyHelper.Copy( typeof( T1 ), _subject, typeof( T2 ), to );
        return to;
    }

    /// 
    /// Copies properties from the passed in object into the subject.
    /// 
    /// The type of object you are copying from.
    /// The object to copy from.</param>
    /// The modified subject that you originally passed in the Copy method.
    public T1 From( T2 from ) where T2 : class
    {
        CopyHelper.Copy( typeof( T2 ), from, typeof( T1 ), _subject );
        return _subject;
    }

    #endregion
}</code></pre>
</div></p>

I would like to constrain T1 and T2 to interfaces at compile time, but I am not sure if that can be done. If you have ideas, please post them in the comments. I thought of using reflection to check if T1 and T2 are interfaces at run time, but I am a big believer in favouring compile time errors over run time errors.

In my next post, I am going to use C# 3.0 extension methods to further simplify copying allowing you to write code like this.

// Copy the data from the customer to the view (using extension methods in C# 3.0)
customer.CopyTo( view );</code></pre>
</div>

I am very interested in hearing your feedback on this, so be sure to post to the comments. Do you think this is a good idea? Do you have suggestions for improvements? Let me know.