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}
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};
What we can do:
1var person = new Person()
2{
3 Name = user.Name,
4 Lastname = user.Lastname
5};
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!
There are two ways we can circumvent this dilemma:
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}
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);
Output will be:
1Person:
2murat
3aykanat
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}
The types match, but the names don't. Our original PropertyCopier
would not work in this scenario.
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}
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}
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}
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);
Output will be:
1Person:
2murat
3aykanat
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}
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);
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);
Outputs of both code snippets will be the same:
1Person:
2murat
3aykanat
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!