Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can one copy the contents of one object to another dynamically if they have the same interface?

Tags:

c#

.net

For example, if I have two objects, one which is of type Monkey and the other of type Dog, and they both implement IAnimal, which is something like this:

interface IAnimal
{
  int numberOfEyes {get; set;}
  string name {get; set;}
}

I want to do something like this:

Monkey monkey = new Monkey() { numberOfEyes = 7, name = "Henry" };
Dog dog = new Dog();
MyFancyClass.DynamicCopy(monkey, dog, typeof(IAnimal));
Debug.Assert(dog.numberOfEyes == monkey.numberOfEyes);

I imagine one can create a class like MyFancyClass using reflection... any clever person have an idea?

Thanks, Stephen

like image 855
Stephen Oberauer Avatar asked Jul 23 '10 14:07

Stephen Oberauer


3 Answers

Just to throw it in the mix... you can also use AutoMapper to map/copy one object to another.... they don't even have to implement the same interface. To make it work automagically, just the names of the properties have to match and then you just do something like:

Mapper.Map<IAnimal, MyClass>(myInstance);
like image 166
Jaime Avatar answered Nov 16 '22 02:11

Jaime


A reflection based solution follows. Note that the reflection work is done only once per Type and then cached, so the overhead should be minimal. Will work with .NET 3.5 and it is not restricted to interfaces.

Note that I use reflection to get all the properties on type T and filter to the properties that have both getters and setters. I then build an expression tree for each property that retrieves the value from the source and assigns that value to the target. The expression trees are compiled and cached in a static field. When the CopyProperties method is called, it invokes the copier for each property, copying all the properties defined in type T.

// Usage
Monkey monkey = new Monkey() { numberOfEyes = 7, name = "Henry" };
Dog dog = new Dog();
DynamicCopy.CopyProperties<IAnimal>(monkey, dog);
Debug.Assert(dog.numberOfEyes == monkey.numberOfEyes);

...    

// The copier
public static class DynamicCopy
{
    public static void CopyProperties<T>(T source, T target)
    {
        Helper<T>.CopyProperties(source, target);
    }

    private static class Helper<T>
    {
        private static readonly Action<T, T>[] _copyProps = Prepare();

        private static Action<T, T>[] Prepare()
        {
            Type type = typeof(T);
            ParameterExpression source = Expression.Parameter(type, "source");
            ParameterExpression target = Expression.Parameter(type, "target");

            var copyProps = from prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                            where prop.CanRead && prop.CanWrite
                            let getExpr = Expression.Property(source, prop)
                            let setExpr = Expression.Call(target, prop.GetSetMethod(true), getExpr)
                            select Expression.Lambda<Action<T, T>>(setExpr, source, target).Compile();

            return copyProps.ToArray();
        }

        public static void CopyProperties(T source, T target)
        {
            foreach (Action<T, T> copyProp in _copyProps)
                copyProp(source, target);
        }
    }
}
like image 6
Greg Avatar answered Nov 16 '22 01:11

Greg


Copy constructor is what I usually do:

class Monkey : IAnimal
{
  public Monkey(IAnimal other)
  {
    //Copy properties here...
  }
}
like image 5
Dr Herbie Avatar answered Nov 16 '22 02:11

Dr Herbie