I need to copy the value of properties from one class to another that are descendants of the same base type. Source and target object may be on different levels of the same inheritance branch, meaning one is derived from the other, or be descendant of different branches, meaning they share a common base type.
A
B1 B2
C1 C2 C3
From the structure above I may want to copy all properties from A to C1, C2 to C3, C3 to B1, etc. Basically any possible combination from the tree. Obviously I can only copy properties present in the source type, that must also be present in the target type.
Iterating the properties of the source type is easy as
var sourceProperties = source.GetType().GetProperties();
However how do I check which property is declared on the target type? Simply checking by name is not enough, as they might have different types. Also in the past I made bad experiences with duplicate properties using new
.
Unfortunately C# or .NET has no built-in method to check if a type has a certain PropertyInfo
like Type.HasProperty(PropertyInfo)
. The best I could come up with is to check if the property was declared by a shared base type.
public static void CopyProperties(object source, object target)
{
var targetType = target.GetType();
var sharedProperties =source.GetType().GetProperties()
.Where(p => p.DeclaringType.IsAssignableFrom(targetType));
foreach (var property in sharedProperties)
{
var value = property.GetValue(source);
if (property.CanWrite)
property.SetValue(target, value);
}
}
Question: Is there a better solution?
Here's a solution that doesn't require inheritance. It copies properties from one object (of one type) to another (of another type) as long as the names and types match.
You create an instance of once of these property copier objects for each pair of types you want to be able to copy from/to. The copier object is immutable once created, so it can be long-lived, static, used from many threads (after created), etc.
Here's the code for the PropertyCopier class. You need to specify the source and destination types when you create an object of this type.
It relies on a helper class that looks like (this can get stripped down; the extra properties where to help with debugging (and might be useful to you)).
I also created another real simple class for testing:
With all that in place, this code exercises the copier class:
When you run it, all the properties of the source that have a property in the destination that have the same name and type will get copied.
If you want to restrict the source and destination types to a common base class, you can do this:
If you don't want two classes, just declare the original
PropertyCopier
class with the three type parameters above, and that set of generic constraints.