I ran into a situation a while ago that I needed to retrieve a serialized object from the database, copy its properties to another object and send that object over the wire. Why the copying? The object stored in the database were written in .NET 2.0, whereas the service I was sending the object to expected .NET 3.5 (WCF) objects.

It was also perfectly possible that the .NET 3.5 objects, which were being developed by another team, would have had properties added or removed without notification.

The following code allows you to copy property values from one object into another. If a property does not exist in the target object, it is ignored. I ran into some issues with indexed properties (aka lists and arrays) which do not allow you to copy the properties of their contained items. So I do some additional reflection magic there to build the array or list dynamically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Private Function CopyProperties(Of sourceType As {Class, New}, targetType As {Class, New})(ByVal source As sourceType, ByVal target As targetType) As targetType
  Dim retValue As New targetType
 
  Dim sourceProperties() As PropertyInfo = source.GetType().GetProperties()
  Dim targetProperties() As PropertyInfo = GetType(targetType).GetProperties()
 
  For Each sourceProp As PropertyInfo In sourceProperties
    For Each targetProp As PropertyInfo In targetProperties
      If sourceProp.Name <> targetProp.Name Then Continue For
 
      ' Only try to set property when able to read the source and write the target
      If sourceProp.CanRead And _
            targetProp.CanWrite Then
        ' We want to leave System types alone
        If sourceProp.PropertyType.IsClass And _
              Not sourceProp.PropertyType.FullName.StartsWith("System.") Then
          Dim sT As Type = Type.GetType(sourceProp.PropertyType.AssemblyQualifiedName)
          Dim tT As Type = Type.GetType(targetProp.PropertyType.AssemblyQualifiedName)
 
          Dim params(0 To 1) As Object
          ' If property is an array or a list, we need to treat it different
          If sourceProp.GetIndexParameters().Length = 0 Then
              params(0) = sourceProp.GetValue(source, Nothing)
              params(1) = targetProp.GetValue(target, Nothing)
 
            targetProp.SetValue(retValue, _
                                GenericCopyProperties(sT, tT).Invoke(Me, params), _
                                Nothing)
          Else
            Dim count As Integer = GetType(sourceType).InvokeMember("Count", BindingFlags.GetProperty, Nothing, source, Nothing)
            For i As Integer = 0 To (count - 1)
              Dim obj As Object() = New Object() {i}
              params(0) = sourceProp.GetValue(source, obj)
              If target IsNot Nothing Then
                params(1) = targetProp.GetValue(target, obj)
              End If
 
              retValue.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, Nothing, _
                                              retValue, New Object() {GenericCopyProperties(sT, tT).Invoke(Me, params)})
            Next
          End If
        Else
            targetProp.SetValue(retValue, sourceProp.GetValue(source, Nothing), Nothing)
        End If
      End If
 
      Exit For
    Next
  Next
 
  Return retValue
End Function

The function above is a generic function as I needed to call some methods on the Type of the objects. I wrapped the dynamic creation of the generic method in the following function.

1
2
3
Private Function GenericCopyProperties(ByVal sourceType As Type, ByVal targetType As Type) As MethodInfo
  Return Me.GetType().GetMethod("CopyProperties").MakeGenericMethod(sourceType, targetType)
End Function

Lastly, the snippet below shows how to call the function.

1
2
3
4
5
Dim params(0 To 1) As Object
params(0) = sourceObject
params(1) = targetObject
 
targetObject = GenericCopyProperties(sourceObject.GetType(), targetObject.GetType()).Invoke(Me, params)

Personally, I love the mix of different techniques here. Reflection, dynamic methods, recursion and generics are very powerful on their own, but combined there’s some real magic waiting to be discovered πŸ™‚

If you liked this post, please click on one of the advertisements below. Thanks!


9 Responses to “Copying class properties using .NET Reflection”

  1. 1 Joey ChΓΆmpff

    Erik, good code you could speed up the code if cache the generic methods. πŸ˜‰

  2. 2 Erik Burger

    Hi Joey,

    That would definately be the first thing I’d do if performance would become an issue.

    Erik

  3. 3 Jimi J

    Does this do a deep or shallow copy?

  4. 4 Jimi J

    I am trying to copy a LINQ Entity class and am getting an error in the GenericCopyProperties function. The MakeGenericMethod’s sourceType parameter is null when it hits that line of code (though it is populated beforehand). Any help would be greatly appreciated.

  5. 5 Erik Burger

    Hi Jimi,

    This *should* do a deep copy.

    As for the error copying your LINQ Entity class, if you could send me the code you are working on I’ll be more than happy to take a look. My email address is eburger at antares dot nl.

    Cheers,

    Erik

  6. 6 Paulo Cancela

    Hi Jimi,

    Great coding !!

    Do you have it in C#, can you send it to me? I’m a little bit lazy today πŸ™‚

    Thanks

    PC

  7. 7 Erik Burger

    Hi Paulo,

    You just reminded me to update my post to include the C# code..the project I worked on while I created this was in VB. In the meanwhile, check out this link and see how far you get πŸ™‚

    http://www.developerfusion.com/tools/convert/vb-to-csharp/

    Erik

  8. 8 Jeff

    Hi Erik,

    I know this is a very old thread, but I am getting an error on line:

    Return Me.GetType().GetMethod(“CopyProperties”).MakeGenericMethod(sourceType, targetType)

    stating “Object reference not set to an instance of an object.” Do you have any thoughts as to why I might be getting this error? The form and source object is already created and I am not calling this function on a form load event.

    Thanks!
    Jeff

  9. 9 Erik Burger

    Hi Jeff,

    The easiest way to figure this out is to check if each object involved is not null. So Me, then whatever is returned from GetType(), then GetMethod(…) etc. If I’d have to guess I’d say Me doesn’t exist in ASP.Net pages but I could be wrong..my strength is C# not VB.

    Let me know how you get on.

    Erik

Leave a Reply


*