Seeing as duck-typing is a buzz topic and quite the rage at the moment, I thought I'd try and introduce my own spin on this, namely duck-copying. As the title suggests, it's more like copying values between objects of different types having have properties that are named the same and have the same type, using a copy constructor and an extension method. I blogged about C# copy constructors here, and this could be seen as an improvement.
To set up the scenario, consider this: You have heavy-weight domain (a.k.a business-logic) objects that you’ve implemented using the Composite Design Pattern. A (contrived) example is a Person class, with the usual attributes like first name, age, etc, but additional properties like a list of address, and a list of movies the personhas seen.
You're communicating with with services (WCF/Web), and you make use of light-weight DTO's to send info up and down the wire, because send a Person object with a list of 300 movies and a couple of addresses would just be unnecessary.
Ultimately, you end up writing code in your PersonDTO overloaded constructor, which takes a Person object, like this:
public PersonDTO (Person person)
{
this.FirstName = person.FirstName;
this.LastName = person.LastName;
this.Age = person.Age;
this.Alias = person.Alias;
// and some more, just for good measure...
}
For good measure and punishment, you may even have the reverse written in the constructor of the person object. Base on the code in the previous blog, I've come up with the following:
Expand Code
public static class ObjectExtensions
{
/// <summary>
/// Will perform a "Duck" copy of two objects, copying values of properties that occur in the source and destination,
/// where the properties are of the same type and have the same name.
/// </summary>
public static void DuckCopyTo(this object source, object destination)
{
PropertyInfo[] propertyInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var propertyInfo in propertyInfos)
{
PropertyInfo destinationPropertyInfo = destination.GetType().GetProperty(propertyInfo.Name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
if (destinationPropertyInfo != null)
{
if (destinationPropertyInfo.CanWrite && propertyInfo.CanRead && (destinationPropertyInfo.PropertyType == propertyInfo.PropertyType))
destinationPropertyInfo.SetValue(destination, propertyInfo.GetValue(source, null), null);
}
}
}
}
This method, implemented as an extension method, will allow you to do the following, which is an improved copy constructor for C#:
public Person (Person original)
{
original.DuckCopyTo(this);
}
In addition, and the point of this article, is the ability to do this in your overloaded PersonDTO constructor:
public PersonDTO (Person person)
{
person.DuckCopyTo(this);
}