17

Anyone have a suggestion for a good utility class that maps values from one object to another? I want a utility class that uses reflection and takes two objects and copies values from the 1st object to the second if there is a public property with the same name.

I have two entities that are generated from a web service proxy, so I can't change the parent class or impliment an interface or anything like that. But I know that the two objects have the same public properties.

1
  • 5
    Just for the record - the accepted answer will have more overheads than the others, since it uses raw reflection. Approaches like AutoMapper and Expression pre-compile this into IL at runtime, which can yield significant performance advantages. So if you are doing lots of this, perhaps avoid the basic reflection approach. Commented Apr 13, 2010 at 5:18

6 Answers 6

41

Should be pretty simple to throw together...

public static void CopyPropertyValues(object source, object destination)
{
    var destProperties = destination.GetType().GetProperties();

    foreach (var sourceProperty in source.GetType().GetProperties())
    {
        foreach (var destProperty in destProperties)
        {
            if (destProperty.Name == sourceProperty.Name && 
        destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
            {
                destProperty.SetValue(destination, sourceProperty.GetValue(
                    source, new object[] { }), new object[] { });

                break;
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

this works on me but i added a line of code for readonly properties.
Works fine for me.. Vote up! Great tip! ;)
As @VincentDagpin said && destProperty.CanWrite for readonly props in if statement
If the Object contain List<string> Property,will it work?
Has anyone a solution for a "deep copy" (with sub objects)?
20

Jon Skeet and Marc Gravell have a library called MiscUtil. Inside MiscUtil.Reflection there is a class called PropertyCopy that does exactly what you describe. It only works for .NET 3.5.

It works by running over the public properties of the SourceType, matches them up by name with the public properties of the TargetType, makes sure that each property can be assigned from the source to the target and then creates and caches a copier function for those two types (so you don't do all this reflection every time). I've used it in production code and can vouch for its goodness.

What the hey, I figured I'd just post their concise code (it's less than 100 lines w/comments). The license for this code can be found here:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace MiscUtil.Reflection
{
    /// <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>.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.
        /// </summary>
        private static class PropertyCopier<TSource> where TSource : class
        {
            private static readonly Func<TSource, TTarget> copier;
            private static readonly Exception initializationException;

            internal static TTarget Copy(TSource source)
            {
                if (initializationException != null)
                {
                    throw initializationException;
                }
                if (source == null)
                {
                    throw new ArgumentNullException("source");
                }
                return copier(source);
            }

            static PropertyCopier()
            {
                try
                {
                    copier = BuildCopier();
                    initializationException = null;
                }
                catch (Exception e)
                {
                    copier = null;
                    initializationException = e;
                }
            }

            private static Func<TSource, TTarget> BuildCopier()
            {
                ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
                var bindings = new List<MemberBinding>();
                foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties())
                {
                    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.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)));
                }
                Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
                return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile();
            }
        }
    }
}

5 Comments

@Marc Gravell: Like the Illuminati? :)
a bit like that, but they don't have the ♦ ;-p (unless Jeff is the Illuminati...)
@Marc Gravell: . .. ... And suddenly their entire plan has become clear.
Any idea how to update this to be able to copy from sql server char(1) column to a char property on my POCO? Right now I'm getting the error "Property EmailType has an incompatible type..." Thanks.
Something to be careful of, the function runs into issues if you are trying to mix nullable and non-nullable types with the same name. The error that is given is a generic Verification Exception generated by the runtime.. took me a while to work out exactly what the problem was
11

We use Automapper for this. It works really well.

1 Comment

Bugger. Took me forever to remember it was "AutoMapper" and not "AutoMap". Btw, searching for "automatically map" on Google sucks.
5

I've improved Robinson's answer and refactored it into an extension method on the Object type, very convenient:

    public static void CopyPropertyValues( this object destination, object source )
    {
        if ( !( destination.GetType ().Equals ( source.GetType () ) ) )
            throw new ArgumentException ( "Type mismatch" );
        if ( destination is IEnumerable )
        {
            var dest_enumerator = (destination as IEnumerable).GetEnumerator();
            var src_enumerator = (source as IEnumerable).GetEnumerator();
            while ( dest_enumerator.MoveNext () && src_enumerator.MoveNext () )
                dest_enumerator.Current.CopyPropertyValues ( src_enumerator.Current );
        }
        else
        {
            var destProperties = destination.GetType ().GetRuntimeProperties ();
            foreach ( var sourceProperty in source.GetType ().GetRuntimeProperties () )
            {
                foreach ( var destProperty in destProperties )
                {
                    if ( destProperty.Name == sourceProperty.Name 
                        && destProperty.PropertyType.GetTypeInfo ()
                            .IsAssignableFrom ( sourceProperty.PropertyType.GetTypeInfo () ) )
                    {
                        destProperty.SetValue ( destination, sourceProperty.GetValue (
                            source, new object[] { } ), new object[] { } );
                        break;
                    }
                }
            }
        }
    }

1 Comment

Suppose we have a list of object. we get an exception after while, dest_enumerator.Current, .......... "Enumeration has either not started or has already finished." your code is not work.
4

What about doing it with Json.net?

static T CopyPropertiesJson<T>(object source)
{
    string jsonsource = JsonConvert.SerializeObject(source);
    return JsonConvert.DeserializeObject<T>(jsonsource);
}

Comments

2

I blogged about using Expression<T> to do this shortly after reading this article by Marc Gravell.

It looks (based on another answer) like it may be similar to the one in Jon Skeet and Marc's MiscUtil.

2 Comments

Funnily enough, I was invite to write that article after somebody liked an Expression<> based bit of code that compares objects property-by-property. I might be wrong, but probably this one: stackoverflow.com/questions/986572/… .
@jitbit fixed URL

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.