Is it possible to copy the contents of one object to another dynamically if they have the same interface?

For example, if I have two objects, one of which is of type Monkey, and the other of type Dog, and both of them implement IAnimal, 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 suppose you can create a class like MyFancyClass using reflection ... does any smart person have an idea?

Thanks Steven

+4
source share
8 answers

The following solution is based on reflection. Note that reflection work is only done once for each type and then cached, so the overhead should be minimal. Will work with .NET 3.5 and is not limited to interfaces.

Note that I use reflection to get all properties of type T and filter properties that have both getters and setters. Then I create an expression tree for each property that extracts a value from the source and assigns that value to the target. Expression trees are compiled and cached in a static field. When the CopyProperties method is called, it calls 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); } } } 
+6
source

Just to drop it into the mix ... you can also use AutoMapper to map / copy one object to another ... they don’t even need to implement the same interface. To make it work automatically, just the property names must match, and then you just do something like:

 Mapper.Map<IAnimal, MyClass>(myInstance); 
+6
source

The copy constructor is what I usually do:

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

You have several options:

You can go the way of using reflection, but it will be much slower than other options, and you will have to process the yoiur reflection code. Make a good general code to β€œclone” using reflection is non-trivial, especially if you need to start feeding for objects containing lists / arrays / dictionaries of other object instances.

Copy constructor, as Dr. Herbie mentioned, is one option.

Another would be to implement ICloneable for all of your types (you could implement the ICloneable interface to force all IAnimals to implement it). It may not be dynamic, like reflection (you would need to process it for each class), but assuming that you just copy the property values, it will be faster than reflection.

It is also worth considering immutability. If you can make your specific types immutable (using readonly on all fields so that they cannot be changed), you probably don't need to worry about cloning at all. Everything can happily share the same safe copy, knowing that no other specialist can modify it in any way. Such immutability can be very powerful, although you need to be careful if your interface contains collections / arrays that you can modify.

Finally, if you have many classes, you can look at the code generation for generating C # cloning classes (the task of which is to generate a clone of this type) and compile them into an assembly. You can use reflection here to create a "cloner class template", but since it generates code (which compiles with the rest of your project), you do not have time to execute a slow reflection.

Thus, there are many options for cloning - but using reflection, even if it can be naive and dynamic, is often not the best approach.

+2
source

You can use IAnimal Implement ICloneable. Then make memeberwise clone on a monkey or another class that implements ICloneable. By the way, this is a shallow copy.

 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; } } 
+1
source

Why not just implement the method in IAnimal ?

(EDIT: As commentators note, IAnimal must be converted to the Animal abstract base class for this solution. This makes sense anyway, since the whole conclusion behind this question is that the child classes contain the 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 with the help of reflection becomes erratic. Should you copy only objects? What about get / set methods? What about read-only properties? This is why it is probably best to define the behavior that you really want at each level. Then you can override at lower levels.

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

As long as DynamicCopy accepts IAnimal as a method parameter, you can do this. But it really helps to understand what you are trying to do.

0
source

Try to find struct methods

0
source

Source: https://habr.com/ru/post/1316671/


All Articles