Method to Copy Data Between Objects of Different Types

2008, Jul 09    

One thing that I find tiresome when using the various Model/View patterns is the constant copying of data between the model and the view. Too often, I find myself writing code like this to copy data between an ICustomer and an ICustomerView;

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

I would much rather write something like one of the following lines;

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

// Copy the data from the customer to the view (using reflection and generics in .NET 2.0)
Copier.Copy( customer ).To( view );

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

It got me to thinking that there must be a better way, so I began writing code that would do the grunt work for me. Too often,

Over the next few days, I will blog about my thought process in developing this method and take it through the various iterations that can be seen in lines 2, 5 & 8 above.

Today, I will start with the .NET 1.x version. I will start with some design decisions;

  • I want to be able to specify the types that I am copying between, not infer them using reflection. This way, I can use the interfaces, not the concrete classes when I am copying between the objects.
  • For now, I am going to assume that if both interfaces have a non-static get/set property with the same name and type I will copy between them.
  • I need to check that neither object is null and that I am not trying to copy an object over to itself.

This was simple enough. I created a static helper class called CopyHelper with one static Copy method. I use Type.GetProperties to get the non-static, public properties with getters and setters. If the name and type match, I use the GetValue and SetValue methods on the PropertyInfo class to copy the value across from one object to the next. This is the result;

#region Copyright © Alteridem Consulting 2008
//
// All rights are reserved. Reproduction or transmission in whole or in part, in
// any form or by any means, electronic, mechanical or otherwise, is prohibited
// without the prior written consent of the copyright owner.
//
// Filename: CopyHelper.cs
// Date:     06/06/2008 11:18 AM
// Author:   Rob Prouse
//
#endregion

#region Using Directives

using System;
using System.Collections.Generic;
using System.Reflection;

#endregion

namespace Alteridem.ModelViewHelpers
{
    public static class CopyHelper
    {
        #region Private Members

        // We are interested in non-static, public properties with getters and setters
        private const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty;

        #endregion

        /// 
        /// Copies all public properties from one object to another.
        /// 
        /// The type of the from object, preferably an interface. We could infer this using reflection, but this allows us to contrain the copy to an interface.</param>
        /// The object to copy from</param>
        /// The type of the to object, preferably an interface. We could infer this using reflection, but this allows us to contrain the copy to an interface.</param>
        /// The object to copy to</param>
        public static void Copy( Type fromType, object from, Type toType, object to )
        {
            if ( fromType == null )
                throw new ArgumentNullException( "fromType", "The type that you are copying from cannot be null" );

            if ( from == null )
                throw new ArgumentNullException( "from", "The object you are copying from cannot be null" );

            if ( toType == null )
                throw new ArgumentNullException( "toType", "The type that you are copying to cannot be null" );

            if ( to == null )
                throw new ArgumentNullException( "to", "The object you are copying to cannot be null" );

            // Don't copy if they are the same object
            if ( !ReferenceEquals( from, to ) )
            {
                // Get all of the public properties in the toType with getters and setters
                Dictionary<string, PropertyInfo> toProperties = new Dictionary<string, PropertyInfo>();
                PropertyInfo[] properties = toType.GetProperties( flags );
                foreach ( PropertyInfo property in properties )
                {
                    toProperties.Add( property.Name, property );
                }

                // Now get all of the public properties in the fromType with getters and setters
                properties = fromType.GetProperties( flags );
                foreach ( PropertyInfo fromProperty in properties )
                {
                    // If a property matches in name and type, copy across
                    if ( toProperties.ContainsKey( fromProperty.Name ) )
                    {
                        PropertyInfo toProperty = toProperties[fromProperty.Name];
                        if ( toProperty.PropertyType == fromProperty.PropertyType )
                        {
                            object value = fromProperty.GetValue( from, null );
                            toProperty.SetValue( to, value, null );
                        }
                    }
                }
            }
        }
    }
}</code>

Using this class, you can now write code like in line 2 of the second listing above. In my next post, I am going to extend this code using generics and give it a fluent interface for better readability.

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.