Method to Copy Data Between Objects of Different Types
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.