The Darkside

Shedding light on things and stuff

 
  Home :: Contact :: Syndication  :: Login
  75 Posts :: 0 Stories :: 49 Comments :: 2 Trackbacks

Ads

Archives

Post Categories

Open Source Projects

Other Blogs

I feel that one of the short-comings in C# is the lack of default copy constructors for classes and, to add to my frustration, the help provided in MSDN about this topic just doesn't cut it. I decided to implement my own "generic" copy constructor, which is by no means feature-complete, but certainly delivers what I need now.

UPDATE 2008 SEP 23: I've updated the copy mechanism in this article. Rather use it than the example in this code.

To start off, I've created a test class.

public class Foo

{

    public string Alias { get; set; }

    public Guid Id { get; set; }

    public int Rating { get; set; }

}

Now, I need to add a constructor to Foo that'll accept another instance of Foo.

public Foo (Foo source){}

According to the MSDN article metioned above, round about here is when you start coding manual assignment statements for every single property that your object has. Instead of that, I've made use of reflection to get and set property values.(Exception handling/parameter checking excluded for code-clarity purposes)

public Foo (Foo source)
{
    PropertyInfo[] propertyInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (var propertyInfo in propertyInfos)
    {
        propertyInfo.SetValue(this, propertyInfo.GetValue(source, null), null);
    }
}

What this code is doing is getting information about all the properties in the class that are public and instance variables. It then loops through them, getting the values from the object passed in, and setting them on the destination object, which in this case is "this".

Because this code is type-agnostic, I then refactored it out to a static method in a seperate class as follows:

public static void CopyObjectValues<T> (T source, T destination) 
{
    PropertyInfo[] propertyInfos = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (var propertyInfo in propertyInfos)
    {
        propertyInfo.SetValue(destination, propertyInfo.GetValue(source, null), null);
    }
}

...and modified the copy constructor to look like this:

public Foo (Foo source)
{
    ObjectUtilities.CopyObjectValues(source, this);
}

You can now use this static method in an class that you need to implement your own copy constructor on.

posted on Thursday, September 04, 2008 9:25 AM

Feedback

# re: Implementing your own copy constructor in c# 9/4/2008 10:34 AM Nik
Very useful - I've often wondered about why it got lost on the way from C++ to C#.

Did you come across any rationale from Anders et al? Perhaps because of the confusion when people might expect deep copies of referenced objects...?

# re: Implementing your own copy constructor in c# 9/4/2008 11:04 AM Darksider
I'm not sure if my Googling capabilities have gone downhill, but I can't seem to find any reasons given by said persons that indicate the decision to leave that functionality out.

# re: Implementing your own copy constructor in c# 9/4/2008 11:04 AM FryHard
Nik seems to have beaten me to the punch!

The problem comes in when the object that you are copying has complex type properties. These properties are shallow copied and can cause unexpected side effects when they are changed.
If Foo had a property ChildFoo.
public class Foo
{
public string Alias { get; set; }
public Guid Id { get; set; }
public int Rating { get; set; }
public Foo ChildFoo { get; set; }
}

The following code will function in a rather unexpected way.
Foo childFoo = new Foo { Alias = "child", Id = Guid.NewGuid(), Rating = 1};
Foo original = new Foo { Alias = "original", Id = Guid.NewGuid(), Rating = 0, ChildFoo = childFoo };

Foo copy = new Foo(original);
copy.Alias = "changed";
copy.ChildFoo.Alias = "changed child";

Assert.AreEqual(original.ChildFoo.Alias, copy.ChildFoo.Alias); // #1
Assert.AreEqual(original.Alias, copy.Alias); //#2
The Assert #1 will return true, while Assert #2 will return false.

The trick would be to prevent this problem with some sort of deep copy.

# re: Implementing your own copy constructor in c# 9/4/2008 12:17 PM Nik
But how far down the rabbit hole do you go... ultimately there's going to be something not copied, which will lead to something unexpected.

If you have responsible programmers, this is very useful code.

For more businessy objects, perhaps a factory pattern would produce more expected results? Also, provides a good opportunity for documentation about what the client should expect.

# re: Implementing your own copy constructor in c# 9/22/2008 11:07 AM Savage
Problem:
The class contains properties that have no setter.

you should use the propertyInfo.CanWrite to check before calling SetValue.

ie:

if (propertyInfo.CanWrite)
propertyInfo.SetValue(this, propertyInfo.GetValue(source, null), null);




# re: Implementing your own copy constructor in c# 9/23/2008 7:55 AM Darksider
@Savage: Yeah, you're right. I've actually updated this code in a later article here

Comments have been closed on this topic.