0

I want to pass in an instance of an interface to an object and initialise all the values of this object to those of the object passed in where both objects implement the same interface? Are there any good shortcuts in this particular case where they share an interface. It seems to me there must be... I just can't recall ...

EDIT: After John's feedback, the question is better expressed as - How do I pass in an instance of an interface to an object's constructor and initialise all the values of this object to those of the interface instance passed in?

Most deep copying solutions (including my own previous solutions) return an object - which is not going to work in a constructor, or rely upon creating a new instance (which is not going to work with an interface as the source and destination).

I want to pass in both source and destination and have properties of the source copied to the destination where they are both interfaces. Is there an existing solution for this. Or do I revisit my own code and try to adapt it - my previous own solution from 2009 (with minor bug corrected in the answers) SetValue on PropertyInfo instance error "Object does not match target type" c# AND svics answer in transfering one object properties values to another one suffice for the simple cases where all properties are just values.

e.g.

    public interface ISomething
    {
        ...
    }

    public class A : ISomething
    {

        public A(ISomething input)
        {

            // what goes here??
        }
    }
Community
  • 1
  • 1
kpollock
  • 3,899
  • 9
  • 42
  • 61
  • 2
    No, there are no C# features for this. There may be mapping libraries that could do this, but there are various cases where you'd need to provide a *lot* more information. What if there are methods in the interface as well as properties? What if a property is a complex object - are you happy just to copy the reference, or do you need a deep clone? – Jon Skeet May 08 '17 at 08:03
  • 1
    @JonSkeet: ... And what if certain properties have to be assigned in a specific order (think minimum and maximum before value)? – O. R. Mapper May 08 '17 at 08:05
  • Ok, I get the point. I am of old aware of all the issues and familar with the existence of MiscUtil.Reflection.PropertyCopy and similar copying implementations (including my own over the years) to deal with all the issues mentioned. I guess my question becomes something different. I will edit to clarify. – kpollock May 08 '17 at 08:10
  • Gosh, I'd forgotten all about MiscUtil.Reflection.PropertyCopy. It certainly doesn't try to cope with any of that though... – Jon Skeet May 08 '17 at 08:28
  • Note that this would be a much, much clearer question if you'd provide a concrete example rather than just "..." in the interface. – Jon Skeet May 08 '17 at 08:28
  • what's the successor to Property Copy, out of interest? – kpollock May 08 '17 at 08:42
  • And I am after having a generic solution, hence the "..." – kpollock May 08 '17 at 08:44

2 Answers2

2

I'm not really sure I fully understand your restrictions, but for most object copying work I use AutoMapper, which greatly helps with the grunt work of copying objects. It means a different approach than copying properties in constructors, but maybe useful. Here's some example code:

public interface ISomething {
    string MyProperty { get; set; }
    int AnotherProperty { get; set; }

    B ClassProperty { get; set; }
}

public class A : ISomething {
    public string MyProperty { get; set; }
    public int AnotherProperty { get; set; }
    public B ClassProperty { get; set; }
}

public class B {
    public string BProperty_1 { get; set; }
    public int BProperty_2 { get; set; }
}

class Program {
    static void Main(string[] args) {

        // Configure the mapping
        Mapper.Initialize(cfg => cfg.CreateMap<ISomething, ISomething>());

        // Initialize first instance
        var firstA = new A {
            MyProperty = "Test",
            AnotherProperty = 21,

            ClassProperty = new B {
                BProperty_1 = "B String",
                BProperty_2 = 555
            }
        };

        // Initialize second instance and perform the mapping
        var secondA = Mapper.Map<ISomething>(firstA);

Here, all the properties in firstA are copied over to secondA, including the properties in ClassProperty.

The mapping configuration is performed once on startup, and uses recursion and reflection to build the mapping model. It can then be used anywhere in your code. If new properties are added to the interface, the mapping configuration stays the same.

Eric Yeoman
  • 1,036
  • 1
  • 14
  • 31
  • I can't recall what I didn't like about AutoMapper the last time I used it but it is definitely the right "ready-rolled" solution. I might give it another go! – kpollock May 08 '17 at 11:00
  • Glad this has helped. It's worth delving deeper into AutoMapper for more complex situations, but for practically everything I do there is a quite simple way of doing it. And the time I've spent learning it has paid back many times over in saved time, and sanity. Definitely when constructing view models as well, where as you point out above there can be a *lot* of properties! – Eric Yeoman May 08 '17 at 11:07
0

Simply set all the properties of the interface in the constructor:

public class A : ISomething
{
    public A(ISomething input)
    {
        A.MyProperty = input.MyProperty;
        A.AnotherProperty = somethingNotFromTheInterface
    }
}

This is called a copy-constructor. Wheather this actually creates a deep or a shallow copy of your existing instance depends on if it contains references to other reference-types. In this case you´d have to re-create all those instances also:

    public A(ISomething input)
    {
        A.MyProperty = new MyType(input.MyProperty);
        A.AnotherProperty = somethingNotFromTheInterface
    }

Which itself assumes you hacve a copy-constructor for the type of MyProperty also.

This can become some huge task when your interface is quite big. You may consider looping all the interfaces properties with reflection in this case, or even better rethink if your interface is actually serving a single purpose and not doing too much.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • i am trying to avoid precisely the cumbersome repetitiousness of this approach, and to find a general solution. – kpollock May 08 '17 at 08:11
  • See my last paragraph for this. – MakePeaceGreatAgain May 08 '17 at 08:12
  • See the edit and John's comment above. It is a view model. Sometimes there just ARE that many fields and you do want them all :-) – kpollock May 08 '17 at 08:20
  • The approach is the same for interfaces. You need a way to copy all the stuff from one instance to a new one, so either build a copy-constructor or implement the `IClone`-interface. However you can stiill use reflection to loop all the properties to be copied. – MakePeaceGreatAgain May 08 '17 at 08:31
  • Concrete examples would be nice (bearing in mind that I know the principles and have written several deep property copiers in my time) - as far as I know C# doesn't have copy constructors or did I miss something? – kpollock May 08 '17 at 08:43
  • It doesn´t matter if you´re dealing with instances of interfaces or classes, ass the former actually are also instances of a class that has members. So the point that you´re specifically interested on copying interface-instance *isn´t actually a point*. Anyway my answer exactly shows how to build a copy-constructor in C#, I´m not sure what you´re still missing. You got everything you need, as far as I can see this. There is no generic solution to build an instance of `A` from `ISomething`, you need to provide some copying-logic. – MakePeaceGreatAgain May 08 '17 at 09:01
  • For some generic object copying solutions already out there it DOES matter. But for your type of answer, it does not, you are right. I stated I was trying to avoid the very repetitious and specific-to-the-particular-object solution. If it helps, here is my previous own solution for the very simple case from 2009 (with minor bug corrected in the answers) http://stackoverflow.com/questions/755646/setvalue-on-propertyinfo-instance-error-object-does-not-match-target-type-c-sh or see svics answer here http://stackoverflow.com/questions/9762190/transfering-one-object-properties-values-to-another-one – kpollock May 08 '17 at 09:22
  • 1
    So put those links into your question so that others know what you´ve allready tried. Also append why this approach doesn´t fully satisfy your needs. – MakePeaceGreatAgain May 08 '17 at 09:34