20

What would be the best way to write a generic copy constructor function for my c# classes? They all inherit from an abstract base class so I could use reflection to map the properties, but I'm wondering if there's a better way?

lomaxx
  • 113,627
  • 57
  • 144
  • 179

5 Answers5

23

A copy constructor basically means you have a single parameter, which is the object you're going to copy.

Also, do a deep copy, not a shallow copy.

If you don't know what deep and shallow copies are, then here's the deal:

Suppose you're copying a class that has a single row of integers as field.

A shallow copy would be:

public class Myclass()
{
    private int[] row;
    public MyClass(MyClass class)
    {
        this.row = class.row
    }
}

deep copy is:

public class Myclass()
{
    private int[] row;
    public MyClass(MyClass class)
    {
        for(int i = 0; i<class.row.Length;i++)
        {
            this.row[i] = class.row[i];
        }
    }
}

A deep copy really gets the actuall values and puts them in a new field of the new object, whilst a shallow copy only copies the pointers.

With the shallow copy, if you set:

row[3] = 5;

And then print both rows, both prints will have 5 as value of the 4th number. With a deep copy however, only the first print will have this, since the rows don't have the same pointers.

Vordreller
  • 2,524
  • 10
  • 34
  • 51
20

Avoid reflection if you can. Each class should have the responsibility of copying its own properties, and send it further to the base method.

duffymo
  • 305,152
  • 44
  • 369
  • 561
Øyvind Skaar
  • 1,842
  • 14
  • 22
  • so just to clarify, it'd be best if you had a custom copy constructor on each class that explicitly mapped the properties? – lomaxx Jan 11 '09 at 23:02
  • I would. How hard is it to write? Certainly easier to read at a glance than a reflection-based method. – duffymo Jan 11 '09 at 23:07
  • Yes, I would recommend that approach. – Øyvind Skaar Jan 11 '09 at 23:12
  • 4
    It's not hard to read and it's certainly easy to write, however there are maintenance issues to consider. If you add a new property to the class you need to update the copy constructor and tests which is pretty easy to forget. – lomaxx Jan 11 '09 at 23:51
  • I would disagree about updating the copy constructor. And depending on what the new property changed I'm not sure that you'd need to do more than just re-run your existing tests. Adding another get/set pair doesn't require new unit tests for each. – duffymo Jan 12 '09 at 00:40
  • how would you copy the newly added properties if you don't update the copy constructor? – lomaxx Jan 12 '09 at 01:09
16

You can create a shallow copy efficiently with reflection by pre-compiling it, for example with Expression. For example, like so.

For deep copies, serialization is the most reliable approach.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
11

Here's a constructor that I'm using. Note that this is a shallow constructor, and rather simplistic, due to the nature of my base class. Should be good enough to get you started.

public partial class LocationView : Location
{
    public LocationView() {}

    // base class copy constructor
    public LocationView(Location value) {
        Type t = typeof(Location);
        PropertyInfo[] properties = t.GetProperties();
        foreach (PropertyInfo pi in properties)
        {
            pi.SetValue(this, pi.GetValue(value, null), null);
        }
    }
    public Quote Quote { get; set; }
}
B2K
  • 2,541
  • 1
  • 22
  • 34
  • 1
    One line-able: `foreach (var propertyInfo in typeof(LocationView).BaseType.GetProperties()) propertyInfo.SetValue(this, propertyInfo.GetValue(obj_to_copy_parameter, null), null);` you could also use a field initializer to only ever read GetProperties once. – Chris Marisic Dec 16 '14 at 18:34
  • 3
    @ChrisMarisic I tend to avoid one-liners when it sacrifices read-ability. – B2K Dec 17 '14 at 17:33
  • See also http://stackoverflow.com/questions/14218989/best-way-to-clone-properties-of-disparate-objects I came up with a generic extension to shallow-clone common properties – B2K Dec 17 '14 at 17:42
0

You may reference valueinjecter and fasterflect nuget packages and use:

public class Myclass()
{
    private string _property;
    public MyClass(MyClass obj)
    {
         this.InjectFrom(obj.DeepClone());
    }
}