Apply properties values from one object to another of the same type automatically?
Because I believe Jon's version is a tad too complicated and and Steve's version is too simple, and I like Daniel's idea of an extension class.
Plus a Generic version is pretty but unnecessary as all items are objects.
I would like to volunteer my lean and mean version. Credits to all the above. :D
Code:
using System;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Iterate the Properties of the source instance and
// populate them from their desination counterparts
PropertyInfo[] srcProps = typeSrc.GetProperties();
foreach (PropertyInfo srcProp in srcProps)
{
if (!srcProp.CanRead)
{
continue;
}
PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name);
if (targetProperty == null)
{
continue;
}
if (!targetProperty.CanWrite)
{
continue;
}
if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate)
{
continue;
}
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
continue;
}
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
{
continue;
}
// Passed all tests, lets set the value
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
}
}
}
Usage:
/// <summary>
/// ExampleCopyObject
/// </summary>
/// <returns></returns>
public object ExampleCopyObject()
{
object destObject = new object();
this.CopyProperties(destObject); // inside a class you want to copy from
Reflection.CopyProperties(this, destObject); // Same as above but directly calling the function
TestClass srcClass = new TestClass();
TestStruct destStruct = new TestStruct();
srcClass.CopyProperties(destStruct); // using the extension directly on a object
Reflection.CopyProperties(srcClass, destObject); // Same as above but directly calling the function
//so on and so forth.... your imagination is the limits :D
return srcClass;
}
public class TestClass
{
public string Blah { get; set; }
}
public struct TestStruct
{
public string Blah { get; set; }
}
As I was bored and a linq version was suggested by a comment
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// A static class for reflection type functions
/// </summary>
public static class Reflection
{
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="destination">The destination.</param>
public static void CopyProperties(this object source, object destination)
{
// If any this null throw an exception
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeDest = destination.GetType();
Type typeSrc = source.GetType();
// Collect all the valid properties to map
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeDest.GetProperty(srcProp.Name)
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
&& targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select new { sourceProperty = srcProp, targetProperty = targetProperty };
//map the properties
foreach (var props in results)
{
props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null);
}
}
}
I have a type in MiscUtil
called PropertyCopy
which does something similar - although it creates a new instance of the target type and copies the properties into that.
It doesn't require the types to be the same - it just copies all the readable properties from the "source" type to the "target" type. Of course if the types are the same, that's more likely to work :) It's a shallow copy, btw.
In the code block at the bottom of this answer, I've extended the capabilities of the class. To copy from one instance to another, it uses simple PropertyInfo
values at execution time - this is slower than using an expression tree, but the alternative would be to write a dynamic method, which I'm not too hot on. If performance is absolutely critical for you, let me know and I'll see what I can do. To use the method, write something like:
MyType instance1 = new MyType();
// Do stuff
MyType instance2 = new MyType();
// Do stuff
PropertyCopy.Copy(instance1, instance2);
(where Copy
is a generic method called using type inference).
I'm not really ready to do a full MiscUtil release, but here's the updated code, including comments. I'm not going to rewrap them for the SO editor - just copy the whole chunk.
(I'd also probably redesign the API a bit in terms of naming if I were starting from scratch, but I don't want to break existing users...)
#if DOTNET35
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace MiscUtil.Reflection
{
/// <summary>
/// Non-generic class allowing properties to be copied from one instance
/// to another existing instance of a potentially different type.
/// </summary>
public static class PropertyCopy
{
/// <summary>
/// Copies all public, readable properties from the source object to the
/// target. The target type does not have to have a parameterless constructor,
/// as no new instance needs to be created.
/// </summary>
/// <remarks>Only the properties of the source and target types themselves
/// are taken into account, regardless of the actual types of the arguments.</remarks>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="source">Source to copy properties from</param>
/// <param name="target">Target to copy properties to</param>
public static void Copy<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
PropertyCopier<TSource, TTarget>.Copy(source, target);
}
}
/// <summary>
/// Generic class which copies to its target type from a source
/// type specified in the Copy method. The types are specified
/// separately to take advantage of type inference on generic
/// method arguments.
/// </summary>
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
/// <summary>
/// Copies all readable properties from the source to a new instance
/// of TTarget.
/// </summary>
public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
{
return PropertyCopier<TSource, TTarget>.Copy(source);
}
}
/// <summary>
/// Static class to efficiently store the compiled delegate which can
/// do the copying. We need a bit of work to ensure that exceptions are
/// appropriately propagated, as the exception is generated at type initialization
/// time, but we wish it to be thrown as an ArgumentException.
/// Note that this type we do not have a constructor constraint on TTarget, because
/// we only use the constructor when we use the form which creates a new instance.
/// </summary>
internal static class PropertyCopier<TSource, TTarget>
{
/// <summary>
/// Delegate to create a new instance of the target type given an instance of the
/// source type. This is a single delegate from an expression tree.
/// </summary>
private static readonly Func<TSource, TTarget> creator;
/// <summary>
/// List of properties to grab values from. The corresponding targetProperties
/// list contains the same properties in the target type. Unfortunately we can't
/// use expression trees to do this, because we basically need a sequence of statements.
/// We could build a DynamicMethod, but that's significantly more work :) Please mail
/// me if you really need this...
/// </summary>
private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>();
private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>();
private static readonly Exception initializationException;
internal static TTarget Copy(TSource source)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
return creator(source);
}
internal static void Copy(TSource source, TTarget target)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
for (int i = 0; i < sourceProperties.Count; i++)
{
targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null);
}
}
static PropertyCopier()
{
try
{
creator = BuildCreator();
initializationException = null;
}
catch (Exception e)
{
creator = null;
initializationException = e;
}
}
private static Func<TSource, TTarget> BuildCreator()
{
ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
var bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!sourceProperty.CanRead)
{
continue;
}
PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
if (targetProperty == null)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
}
if (!targetProperty.CanWrite)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
}
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is static in " + typeof(TTarget).FullName);
}
if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
}
bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
sourceProperties.Add(sourceProperty);
targetProperties.Add(targetProperty);
}
Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
}
}
}
#endif
Here's a short and sweet version, since you said both of your objects are of the same type:
foreach (PropertyInfo property in typeof(YourType).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(targetObject, property.GetValue(sourceObject, null), null);
}