Extending CopyHelper using Extension Methods

In my last two posts, I have been developing a small utility library to do the grunt work of copying data from an instance of one class to an instance of another type. The Copier class from my last post allows me to copy all public properties from one class to another class as long as the properties have the same name and type. All that is done with one line of code;

Copier<ICustomer>.Copy( customer ).To<ICustomerView>( view );

Today, I am going to use extension methods to simplify the above code even further. I want to be able to write

customer.CopyTo<ICustomerView>( view );

or if we want to rely on type inferencing with the generic CopyTo method, you could write it as simply as

customer.CopyTo( view );

How is this done? Using extension methods, it was actually much simpler than yesterday’s Copier class. In fact, it just ended up being one line of code for the CopyTo method and for the CopyFrom method. I simply wrapped the CopyHelper class like this.

public static class CopierExtensions
{
    public static void CopyTo<T>( this object from, T to ) where T : class
    {
        CopyHelper.Copy( from.GetType(), from, typeof( T ), to );
    }

    public static void CopyFrom<T>( this object to, T from ) where T : class
    {
        CopyHelper.Copy( typeof( T ), from, to.GetType(), to );
    }
}

The only problem I have with this is that the class that the extension methods are applied to are not constrained by an interface, so all matching properties are copied. Does anyone have any ideas on that?

I have uploaded a copy of the solution for this project along with an NUnit test project. Take a look, use it if you like and feel free to give me suggestions for improvements.

In the next few posts I was thinking of extending these classes even further. I might add attributes that allow you to ignore certain properties, maybe add an attribute that specifies interfaces that you are allowed to copy to, possibly an attribute that allows properties to automatically be converted between types. What would you find useful or like to see? Would you like to see a post on the performance using these methods?

Read more

Extending CopyHelper using Generics

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.

// 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;

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 );

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<ICustomer>.Copy( customer ).To<ICustomerView>( view );

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<T1> where T1 : class
{
    #region Private Members

    private readonly T1 _subject;

    #endregion

    #region Public Interface

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

    #endregion

    #region Construction

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

    #endregion

    #region Copier Methods

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

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

    #endregion
}

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<ICustomerView>( view );

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.

Read more