I had a case today where I needed to clone all properties of an object from one class to another of different types.
The originating type was a LINQ 2 SQL entity class type which represents the data model for my database table and the destination object which needed to be cloned to was a custom POCO object which contained all the same property names for streamlined transport over WCF.
I devised a simple function to do the job and it works well to clone all objects aka. properties in an instantiated object by iterating through each property, testing if the same property name can be found and whether the type is the same between originator and destination, if this is the case, the object’s value shall be cloned.
I’ve tested it under an array of different conditions including primitive types, indexers, enum’s and a generic lists of other object type tests, and as you will see by the code there is no type restrictions, so your object should be cloned with no problems generic or custom as long as it adheres to the golden rule of having a matching name and type between origin and destination.
Hopefully this will come in handy for you guys.
I’ve developed it in three flavours; the first being in the all-important Lamda expression syntax (.NET 3.0+):
///
/// Clone properties from an original object to a destination object.
///
///
///
///
///
public void CloneProperties(T1 origin, T2 destination)
{
// Instantiate if necessary
if (destination == null) throw new ArgumentNullException("destination", "Destination object must first be instantiated.");
// Loop through each property in the destination
foreach (var destinationProperty in destination.GetType().GetProperties())
{
// find and set val if we can find a matching property name and matching type in the origin with the origin's value
if (origin != null && destinationProperty.CanWrite)
{
origin.GetType().GetProperties().Where(x => x.CanRead && (x.Name == destinationProperty.Name && x.PropertyType == destinationProperty.PropertyType))
.ToList()
.ForEach(x => destinationProperty.SetValue(destination, x.GetValue(origin, null), null));
}
}
}
The second being another lambda (.NET 3.0+) implementation but conveniently wrapped up into an extension method (accessible by originObj.CloneProperties(destObj)):
public static class ObjectExtension
{
///
/// Clone properties from an original object to a destination object.
///
///
///
///
///
public static void CloneProperties(this T1 origin, T2 destination)
{
// Instantiate if necessary
if (destination == null) throw new ArgumentNullException("destination", "Destination object must first be instantiated.");
// Loop through each property in the destination
foreach (var destinationProperty in destination.GetType().GetProperties())
{
// find and set val if we can find a matching property name and matching type in the origin with the origin's value
if (origin != null && destinationProperty.CanWrite)
{
origin.GetType().GetProperties().Where(x => x.CanRead && (x.Name == destinationProperty.Name && x.PropertyType == destinationProperty.PropertyType))
.ToList()
.ForEach(x => destinationProperty.SetValue(destination, x.GetValue(origin, null), null));
}
}
}
}
And the more vanilla-flavoured all .NET versions compatible forEach code syntax:
///
/// Clone properties from an original object to a destination object.
///
///
///
///
///
public void CloneProperties(T1 origin, T2 destination)
{
// Instantiate if necessary
if (destination == null) throw new ArgumentNullException("destination", "Destination object must first be instantiated.");
// Loop through each property in the destination
foreach (var destinationProperty in destination.GetType().GetProperties())
{
// find and set val if we can find a matching property name and matching type in the origin with the origin's value
if (origin != null && destinationProperty.CanWrite)
{
foreach (var originProperty in origin.GetType().GetProperties())
{
if (destinationProperty.CanWrite && originProperty.CanRead &&
(originProperty.Name == destinationProperty.Name && originProperty.PropertyType == destinationProperty.PropertyType))
{
destinationProperty.SetValue(destination, originProperty.GetValue(origin, null), null);
}
}
}
}
}
And a complete test case if you’re interested:
public enum Status
{
Active,
Inactive
}
public class Test1
{
public string Name { get; set; }
public decimal Amount { get; set; }
public bool IsRequired { get; set; }
public int[] Categories { get; set; }
public Status Status { get; set; }
public List StringList { get; set; }
public List TestInner { get; set; }
}
public class Test2
{
public string Name { get; set; }
public decimal Amount { get; set; }
public bool IsRequired { get; set; }
public int[] Categories { get; set; }
public Status Status { get; set; }
public List StringList { get; set; }
public List TestInner { get; set; }
}
public class TestInner
{
public string Name { get; set; }
public int Value { get; set; }
}
public void TestClone()
{
// Cloning Test
// Any properties found to have same name and type will be populated.
Test1 test1 = new Test1()
{
Name = "test",
Amount = 50.60m,
IsRequired = true,
Categories = new[] { 5, 10, 15 },
Status = Status.Active,
StringList = new List() { "your", "set", "of", "strings" },
TestInner = new List()
{
new TestInner() { Name = "Test 1 inner #1.", Value = 70 },
new TestInner() { Name = "Test 1 inner #2.", Value = 80 }
}
};
Test1 testClonedSame = new Test1(); // Instantiation to empty not necessary
// Test cloning to same object type
CloneProperties(test1, testClonedSame);
Test2 testClonedDiff = new Test2(); // Instantiation to empty not necessary
// Test cloning to different object type
CloneProperties(test1, testClonedDiff);
// Or if you're too cool for type arguments...
// CloneProperties(test1, testClonedDiff);
}
Enjoy. This should convert to VB.NET if need be with no complications.
4 Comments
Exctly what I was looking for ! Thanks !
No problem!
For a collection type property, it just clone the reference, not a deep clone, so if you add a new item to the cloned collection, the sourcee collection also add a new one, this is what I don’t need, maybe some people also doesn’t need.
Before read your article, I also write such a methond, the main problem is I can’t deep clone a collection type property.
Cool!
Thanks.
3 Trackbacks/Pingbacks
[...] Cloning Object Properties via Reflection « {Programming} & Life [...]
[...] Cloning Object Properties via Reflection « {Programming} & Life [...]
Cloning Object Properties via Reflection « {Programming} & Life…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
Post a Comment