7

I have 2 instances of the same objects, o1, and o2. If I am doing things like

 if (o1.property1 != null) o1.property1 = o2.property1 

for all the properties in the object. What would be the most efficient way to loop through all properties in an Object and do that? I saw people using PropertyInfo to check nulll of the properties but it seems like they could only get through the PropertyInfo collection but not link the operation of the properties.

Thanks.

leppie
  • 115,091
  • 17
  • 196
  • 297

3 Answers3

14

You can do this with reflection:

public void CopyNonNullProperties(object source, object target)
{
    // You could potentially relax this, e.g. making sure that the
    // target was a subtype of the source.
    if (source.GetType() != target.GetType())
    {
        throw new ArgumentException("Objects must be of the same type");
    }

    foreach (var prop in source.GetType()
                               .GetProperties(BindingFlags.Instance |
                                              BindingFlags.Public)
                               .Where(p => !p.GetIndexParameters().Any())
                               .Where(p => p.CanRead && p.CanWrite))
    {
        var value = prop.GetValue(source, null);
        if (value != null)
        {
            prop.SetValue(target, value, null);
        }
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
2

Judging from your example i think your looking for something like this:

static void CopyTo<T>(T from, T to)
{
    foreach (PropertyInfo property in typeof(T).GetProperties())
    {
        if (!property.CanRead || !property.CanWrite || (property.GetIndexParameters().Length > 0))
            continue;

        object value = property.GetValue(to, null);
        if (value != null)
            property.SetValue(to, property.GetValue(from, null), null);
    }
}
Jan-Peter Vos
  • 3,157
  • 1
  • 18
  • 21
  • I would make a generic version of that to guarantee the same type of both objects :) – khellang Nov 03 '12 at 19:36
  • @khellang great idea :), changed my example – Jan-Peter Vos Nov 03 '12 at 19:39
  • @khellang: It doesn't actually guarantee that though. For example, this would compile fine: `CopyTo(new Button(), new Object())`. I originally made my code generic, then removed that as it didn't really help. – Jon Skeet Nov 03 '12 at 19:58
2

If you are going to use this many times, you could use a compiled expression for better performance:

public static class Mapper<T>
{
    static Mapper()
    {
        var from = Expression.Parameter(typeof(T), "from");
        var to = Expression.Parameter(typeof(T), "to");

        var setExpressions = typeof(T)
            .GetProperties()
            .Where(property => property.CanRead && property.CanWrite && !property.GetIndexParameters().Any())
            .Select(property =>
            {
                var getExpression = Expression.Call(from, property.GetGetMethod());
                var setExpression = Expression.Call(to, property.GetSetMethod(), getExpression);
                var equalExpression = Expression.Equal(Expression.Convert(getExpression, typeof(object)), Expression.Constant(null));

                return Expression.IfThen(Expression.Not(equalExpression), setExpression);
            });

        Map = Expression.Lambda<Action<T, T>>(Expression.Block(setExpressions), from, to).Compile();
    }

    public static Action<T, T> Map { get; private set; }
}

And use it like this:

Mapper<Entity>.Map(e1, e2);
dotarj
  • 418
  • 2
  • 8