开发者

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

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 开发者_JAVA技巧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


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);


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);
        }
    }
}


Copy constructor is what I usually do:

class Monkey : IAnimal
{
  public Monkey(IAnimal other)
  {
    //Copy properties here...
  }
}


You have several options here:

You could go down the route of using reflection, but this will be much slower than other options, and you'll have to craft yoiur refleciton code. To make nice generic "clone" code using reflection is non-trivial, especially when you have to start catering for objects that contain lists/arrays/dictionaries of other object instances.

A copy constructor, as Dr Herbie mentioned, is one option.

Another would be to implement ICloneable on all your types (you could make you interface implement ICloneable to force all IAnimals to implement it). This may not be dynamic, like reflection (you'd have to hand craft it for each class), but assuming you just copy the property values accross, it'll be way faster than reflection.

Also worth thinking about is immutability. If you can make your concrete types immutable (using readonly on all fields so they can't be changed), then you probably don't need to worry about cloning at all. Everything can happily share the same instance safe in the knowledge that no other sharer can be modifying it in any way. This sort of immutability can be very powerful, although you need to be careful if your interface contains collections/arrays that can be modified.

Finally, if you have a lot of classes, you could look at code generation to generate C# "cloner" classes (whose job it is to generate a clone of a given type) and compile them into an assembly. You can use reflection here to create the "cloner class template", but since it generates code (that compiles with the rest of your project), you don't have the run-time hit of slow reflection.

So, there are lots of options for cloning - but using reflection, even though it can be naice and dynamic, is often not the best approach.


You could maKe IAnimal Implement ICloneable. Then do a memeberwise clone on the monkey or an other class that implements ICloneable. This is a shallow copy by the way.

public interface IAnmial : ICloneable
{
    string Name{get; set;}
    object Clone();
}

public class Monkey : IAnmial
{
    public string Name{get; set;}

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

public class Dog : IAnmial
{
    public string Name{get; set;}

    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

public class Test()
{
    public void CloneAnimal()
    {
        Dog dog = new Dog()
        {
            Name = "MyAnimal",
        };
        IAnimal monkey = dog.Clone() as IAnimal;
    }
}


As long as DynamicCopy takes in an IAnimal as a method parameter you can do that. But it really helps to understand what you are trying to do.


Why not just implement a method in IAnimal?

(EDIT: As commenters have helpfully pointed out, IAnimal should be converted into an abstract base class Animal for this solution. This makes sense anyway, since the whole inference behind this question is that the child classes contain properties defined in the parent.)

// In Aminal class.
public void CopyAttributes(Animal source)
{
    this.numberOfEyes = source.numberOfEyes;
    this.name = source.name;
}

Doing something like this via reflection gets messy quick. Should you only copy properties? What about get/set methods? What about read-only properties? That is why it is probably best to define the behavior you actually want at each level. You can then override at lower levels if desired.

// In Monkey class.
public void CopyAttributes(Monkey source)
{
    super.CopyAttributes(source);
    this.numberOfTails = source.numberOfTails;
}


Try looking at struct methods

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜