Author avatar

Murat Aykanat

Property Copying Between Two Objects using Reflection

Murat Aykanat

  • Feb 13, 2017
  • 12 Min read
  • 144,797 Views
  • Feb 13, 2017
  • 12 Min read
  • 144,797 Views
Microsoft.NET

The problem

Sometimes you have an object which has a lot of public properties, but you only need a handful of these properties for a particular task, such as creating a CSV file with that object. For example, check out the classes below:

1public class User
2{
3    public string Username{get;set;}
4    public string Address{get;set;}
5    public string Name{get;set;}
6    public string Lastname{get;set;}
7}
8
9public class Person
10{
11    public string Name{get;set;}
12    public string Lastname{get;set;}
13}
csharp

Now, for example, we already have a User object and we want to create a Person object for another task and copy Name and Lastname properties from the User object.

If we have a user object like this:

1var user = new User()
2{
3    Username = "murat",
4    Address = "Some address string here",
5    Name = "murat",
6    Lastname = "aykanat"
7};
csharp

What we can do:

1var person = new Person()
2{
3    Name = user.Name,
4    Lastname = user.Lastname
5};
csharp

However if we have, say, 30 properties on the "parent" object and 15 properties on the "child" object, we have to write 15 lines of repetitious, boring, and clunky code. There must be another way!

The Solution

There are two ways we can circumvent this dilemma:

  • We can copy similarly named properties from the "parent" object to the "child" object using reflection.
  • We can use attributes in the "child" object to "mark" them for the parent object to copy its values to "child" object using reflection.

Property Copying

Let's start with the simple one. We will create a class named PropertyCopier with static method Copy to copy public properties from the parent object to the child object. To make our task easier, we'll only copy similarly-named properties.

In this method we will have two foreach loops iterating over child and parent object's public properties, constantly checking to see if their names and types match. Whenever we find a match, we will copy the value to the child object's corresponding property.

1public class PropertyCopier<TParent, TChild> where TParent : class
2                                            where TChild : class
3{
4    public static void Copy(TParent parent, TChild child)
5    {
6        var parentProperties = parent.GetType().GetProperties();
7        var childProperties = child.GetType().GetProperties();
8
9        foreach (var parentProperty in parentProperties)
10        {
11            foreach (var childProperty in childProperties)
12            {
13                if (parentProperty.Name == childProperty.Name && parentProperty.PropertyType == childProperty.PropertyType)
14                {
15                    childProperty.SetValue(child, parentProperty.GetValue(parent));
16                    break;
17                }
18            }
19        }
20    }
21}
csharp

We can see the usage below:

1var user = new User()
2{
3    Username = "murat",
4    Address = "Some address string here",
5    Name = "murat",
6    Lastname = "aykanat"
7};
8
9var person = new Person();
10
11PropertyCopier<User, Person>.Copy(user, person);
12
13Console.WriteLine("Person:");
14Console.WriteLine(person.Name);
15Console.WriteLine(person.Lastname);
csharp

Output will be:

1Person:
2murat
3aykanat

Property Matching

It is all good when we have similar names and types, but what if we want something more dynamic? We have often classes where copying is useful but names differ from class to class. Furthermore, our current copier gives us little control over which properties actually get copied over to the child class. We may want to avoid sending over certain properties, even if they match in name and type.

Here's an example of some of our current version's limitations:

1public class PersonMatch
2{
3    public string NameMatch { get; set; }
4    public string LastnameMatch { get; set; }
5}
csharp

The types match, but the names don't. Our original PropertyCopier would not work in this scenario.

The better way

We need a way to "mark" the properties that we want to be copied from the parent object. The best way to do it would be using attributes to mark these properties.

Attributes can be defined as metadata. They can be assigned to classes, properties, methods, and so on. They provide metadata on the object they are set on. For example, we can define the maximum length of a string property. When we set a string to the property, it will first check against the metadata we provided beforehand and it will either set it to the new value or not depending on the length of the new string value.

In our case, we will use attributes to define the parent property name. We will tell the code that this subclass property will get its value from the property in the parent instance.

First of all we need to create a class to define our attribute. It will be a simple class that derives from Attribute class. It will have a single public field that defines the matching property name in the parent object. Here is the code for our attribute:

1[AttributeUsage(AttributeTargets.Property)]
2public class MatchParentAttribute : Attribute
3{
4    public readonly string ParentPropertyName;
5    public MatchParentAttribute(string parentPropertyName)
6    {
7        ParentPropertyName = parentPropertyName;
8    }
9}
csharp

With our attribute defined, we can add the necessary attributes to our class that we had before:

1public class PersonMatch
2{
3    [MatchParent("Name")]
4    public string NameMatch { get; set; }
5    [MatchParent("Lastname")]
6    public string LastnameMatch { get; set; }
7}
csharp

All we have to do now check the attribute value of each property in the child object and find the matching name in the parent object:

1public class PropertyMatcher<TParent, TChild> where TParent : class
2                                                  where TChild : class
3{
4    public static void GenerateMatchedObject(TParent parent, TChild child)
5    {
6        var childProperties = child.GetType().GetProperties();
7        foreach (var childProperty in childProperties)
8        {
9            var attributesForProperty = childProperty.GetCustomAttributes(typeof(MatchParentAttribute), true);
10            var isOfTypeMatchParentAttribute = false;
11
12            MatchParentAttribute currentAttribute = null;
13
14            foreach (var attribute in attributesForProperty)
15            {
16                if (attribute.GetType() == typeof(MatchParentAttribute))
17                {
18                    isOfTypeMatchParentAttribute = true;
19                    currentAttribute = (MatchParentAttribute) attribute;
20                    break;
21                }
22            }
23
24            if (isOfTypeMatchParentAttribute)
25            {
26                var parentProperties = parent.GetType().GetProperties();
27                object parentPropertyValue = null;
28                foreach (var parentProperty in parentProperties)
29                {
30                    if (parentProperty.Name == currentAttribute.ParentPropertyName)
31                    {
32                        if (parentProperty.PropertyType== childProperty.PropertyType)
33                        {
34                            parentPropertyValue = parentProperty.GetValue(parent);
35                        }
36                    }
37                }
38                childProperty.SetValue(child, parentPropertyValue);
39            }
40        }
41    }
42}
csharp

Let's try our code and see what we get:

1var user = new User()
2{
3    Username = "murat",
4    Address = "Some address string here",
5    Name = "murat",
6    Lastname = "aykanat"
7};
8
9var personMatch = new PersonMatch();
10
11PropertyMatcher<User, PersonMatch>.GenerateMatchedObject(user, personMatch);
12
13Console.WriteLine("Person:");
14Console.WriteLine(personMatch.NameMatch);
15Console.WriteLine(personMatch.LastnameMatch);
csharp

Output will be:

1Person:
2murat
3aykanat

Property Copying/Matching in extension methods

If we don't want to use PropertyCopier or PropertyMatcher classes we can define extension methods for type object. We will use the same code as above to fill the methods:

1public static class ObjectExtensionMethods
2{
3    public static void CopyPropertiesFrom(this object self, object parent)
4    {
5        var fromProperties = parent.GetType().GetProperties();
6        var toProperties = self.GetType().GetProperties();
7
8        foreach (var fromProperty in fromProperties)
9        {
10            foreach (var toProperty in toProperties)
11            {
12                if (fromProperty.Name == toProperty.Name && fromProperty.PropertyType == toProperty.PropertyType)
13                {
14                    toProperty.SetValue(self, fromProperty.GetValue(parent));
15                    break;
16                }
17            }
18        }
19    }
20
21    public static void MatchPropertiesFrom(this object self, object parent)
22    {
23        var childProperties = self.GetType().GetProperties();
24        foreach (var childProperty in childProperties)
25        {
26            var attributesForProperty = childProperty.GetCustomAttributes(typeof(MatchParentAttribute), true);
27            var isOfTypeMatchParentAttribute = false;
28
29            MatchParentAttribute currentAttribute = null;
30
31            foreach (var attribute in attributesForProperty)
32            {
33                if (attribute.GetType() == typeof(MatchParentAttribute))
34                {
35                    isOfTypeMatchParentAttribute = true;
36                    currentAttribute = (MatchParentAttribute)attribute;
37                    break;
38                }
39            }
40
41            if (isOfTypeMatchParentAttribute)
42            {
43                var parentProperties = parent.GetType().GetProperties();
44                object parentPropertyValue = null;
45                foreach (var parentProperty in parentProperties)
46                {
47                    if (parentProperty.Name == currentAttribute.ParentPropertyName)
48                    {
49                        if (parentProperty.PropertyType== childProperty.PropertyType)
50                        {
51                            parentPropertyValue = parentProperty.GetValue(parent);
52                        }
53                    }
54                }
55                childProperty.SetValue(self, parentPropertyValue);
56            }
57        }
58    }
59}
csharp

To demonstrate the extension methods, we can use the code below:

1var user = new User()
2{
3    Username = "murat",
4    Address = "Some address string here",
5    Name = "murat",
6    Lastname = "aykanat"
7};
8
9var person = new Person();
10
11person.CopyPropertiesFrom(user);
12
13Console.WriteLine("Person:");
14Console.WriteLine(person.Name);
15Console.WriteLine(person.Lastname);
csharp
1var user = new User()
2{
3    Username = "murat",
4    Address = "Some address string here",
5    Name = "murat",
6    Lastname = "aykanat"
7};
8
9var personMatch = new PersonMatch();
10
11personMatch.MatchPropertiesFrom(user);
12
13Console.WriteLine("Person:");
14Console.WriteLine(personMatch.NameMatch);
15Console.WriteLine(personMatch.LastnameMatch);
csharp

Outputs of both code snippets will be the same:

1Person:
2murat
3aykanat

Conclusion

In this guide I explained two ways to copy properties from one object to another. If you face a similar situation in your projects, instead of writing multiple lines of code, you can just use these classes or extension methods to copy the needed properties from one object to the other object.

I hope this guide will be useful for your projects. Please feel free to post your ideas and feedback.

Happy coding!