6

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

Stephen Oberauer
  • 5,237
  • 6
  • 53
  • 75
  • 1
    You can totally do this using reflection. It's also a very bad idea. I think this is a case where explaining why you're trying to do this will allow people to help you find a better solution. – jdmichal Jul 23 '10 at 14:49
  • Btw.: You can define properties in an interface, but not fields (as in your code example). In your case, all properties should have both `set` as well as `get` accessors. – stakx - no longer contributing Jul 23 '10 at 15:12
  • 1
    Are you using .NET 4? There are more power ExpressionTree options in the newest framework and would lend themselves to a well performing reflection-based solution. – Greg Jul 23 '10 at 15:18
  • jdmichal... out of laziness... instead of having a bunch of interface1.value = interface2.value, I'd prefer a dynamic way of doing this. stakx ... yip.. I forget the { set; get; } ... oops :) Greg - .Net 3.5 – Stephen Oberauer Jul 23 '10 at 16:34

8 Answers8

7

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);
Jaime
  • 6,736
  • 1
  • 26
  • 42
6

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);
        }
    }
}
Greg
  • 23,155
  • 11
  • 57
  • 79
5

Copy constructor is what I usually do:

class Monkey : IAnimal
{
  public Monkey(IAnimal other)
  {
    //Copy properties here...
  }
}
Dr Herbie
  • 3,930
  • 1
  • 25
  • 28
  • Shouldn't the copy constructor be at the `IAnimal` level, and the `Monkey` copy constructor delegates to that one? Otherwise, you break encapsulation. – jdmichal Jul 23 '10 at 14:52
  • Except interfaces don't have constructors. If it was an abstract class then I would say that yes, the base class should have it's own copy constructor and derived classes call down to that. – Dr Herbie Jul 23 '10 at 14:55
  • I think something like this warrants making `IAnimal` an abstract base class instead of an interface. Though I really like your extension method idea posted in comment to mine. – jdmichal Jul 23 '10 at 15:00
3

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.

Rob Levine
  • 40,328
  • 13
  • 85
  • 111
  • Reflection doesn't have to be slow. I often use reflection to build an Expression tree, compile it to a lambda function, and cache the delegate. There is a one-time build cost, but all subsequent calls should be as fast as normal code. Example here: http://stackoverflow.com/questions/2685046/uses-for-static-generic-classes/2685856#2685856 – Greg Jul 23 '10 at 15:13
  • @Greg - your are right of course, and that is a good trick (although the delegate invocation is still probably an order of magnitude slower than a native call, it is much much faster than reflection alone). What I was trying to get across though is that it is "non-trivial" to write a decent generic cloner, and there are other approaches worth looking at before diving in with reflection. – Rob Levine Jul 23 '10 at 15:31
  • Agreed. I would only recommend using fancy reflection techniques if the scale of the project makes the other approaches labor intensive or error-prone. – Greg Jul 23 '10 at 15:47
1

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;
    }
}
Thomas
  • 1,177
  • 1
  • 14
  • 26
0

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;
}
jdmichal
  • 10,984
  • 4
  • 43
  • 42
  • 3
    You cannot implement functions in an interface. – riwalk Jul 23 '10 at 14:54
  • I think you might want to re-think that. IAnimal is an interface - no implementation. Think about abstract class that implements IAnimal – Daniel Dyson Jul 23 '10 at 14:54
  • So make it an abstract class. Or a static method. Geeze. – jdmichal Jul 23 '10 at 14:55
  • If you were doing this, then you can add it as an extension method to the IAnimal interface and use type-inferece to automatically get it working on all derived classes (which is a nice trick). – Dr Herbie Jul 23 '10 at 14:56
  • @Dr Herbie See, excellent solution. Never would have thought of that! I'm a little peeved about the -1 for something so easily correctable in multiple ways. I usually only give a -1 for blatantly wrong answers, not for technical details. – jdmichal Jul 23 '10 at 14:58
  • @Stargazer712 Actually, I didn't specify what `IAnimal` is, so I technically didn't write code that wouldn't compile. The point is that my IDEA is valid, even if some unnecessary code snippet I wrote isn't. The fact that I can entirely negate the issue by adding `static` in front of my method declaration is, in my opinion, indicative that the downvote was not deserved. – jdmichal Jul 23 '10 at 15:05
  • The convention is that any name in the format IXxxxx is an interface. The only code showing a declaration was the original author, who declared it as an interface. Face it--you suggested that he use code that is contrary to what interfaces are, and your updated suggestion is not much better. – riwalk Jul 23 '10 at 15:14
  • @Stargazer712 I haven't edited my answer at all to this point. And again, you are not addressing my point that the idea is valid, which is to provide this as a method at the base level and override it in subsequent levels to provide additional attributes. Face it--there is nothing wrong with that idea that is worthy of a downvote. – jdmichal Jul 23 '10 at 15:32
  • @Stargazer712 Whatever I'm done. You refuse to address my point, and you already downvoted me and can't do so again for "attitude". (Even though I simply reflected yours back.) The point of this site is to share ideas and knowledge, which is exactly what I have done here. There is nothing wrong with the idea that I expressed, and I refuse to accept a downvote simply because some code I wrote on the fly in a web text box "wouldn't compile". It's your choice from here whether to continue your attempts to make this site hostile over cooperative. – jdmichal Jul 23 '10 at 17:16
0

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.

JonH
  • 32,732
  • 12
  • 87
  • 145
0

Try looking at struct methods

cheesebunz
  • 570
  • 3
  • 12
  • 23