I am trying to do a deep copy a source object's property values to the destination object's property of the same name. The problem I have run into is is that if the source property is a collection, List(of integer) in this case. It will copy it as a reference and not an independent copy which is expected and can be seen in the output results. I plan to recursively call the Copy function for each property that is a collection to do a deep copy on that object. L'ii also add some associate code.
The problem I am having is I can't detect a List(of) object when doing a type comparison 2) Although this works when doing a Int16 type comparison 1) I can get it to work when I compare the type.Name 3)
Is this 3) robust enough or is there a way to make 2) work?
Output:
Unchanged
Source.ID:1 Dest.ID:1
Source.Description:Source Description Dest.Description:Source Description
Source.Links(0):10 Dest.Links(0):10
Source.Links(1):11 Dest.Links(1):11
changed
Source.ID:1 Dest.ID:100
Source.Description:Source Description Dest.Description:Dest Description
Source.Links(0):50 Dest.Links(0):50
Source.Links(1):11 Dest.Links(1):11
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim Source As New DataClass
Dim SourceLinks As New List(Of Integer)({10, 11, 12})
Source.ID = 1
Source.Description = "Source Description"
Source.Links = SourceLinks
Dim Dest As New DataClass
PropertyCopier(Of DataClass, DataClass).Copy(Source, Dest)
Debug.Print("Unchanged")
Debug.Print("Source.ID:" & Source.ID & vbTab & "Dest.ID:" & Dest.ID)
Debug.Print("Source.Description:" & Source.Description & vbTab & "Dest.Description:" & Dest.Description)
Debug.Print("Source.Links(0):" & Source.Links(0) & vbTab & "Dest.Links(0):" & Dest.Links(0))
Debug.Print("Source.Links(1):" & Source.Links(1) & vbTab & "Dest.Links(1):" & Dest.Links(1))
Debug.Print("changed")
Dest.ID = 100
Dest.Description = "Dest Description"
Dest.Links(0) = 50
Debug.Print("Source.ID:" & Source.ID & vbTab & "Dest.ID:" & Dest.ID)
Debug.Print("Source.Description:" & Source.Description & vbTab & "Dest.Description:" & Dest.Description)
Debug.Print("Source.Links(0):" & Source.Links(0) & vbTab & "Dest.Links(0):" & Dest.Links(0))
Debug.Print("Source.Links(1):" & Source.Links(1) & vbTab & "Dest.Links(1):" & Dest.Links(1))
End Sub
End Class
Public Class DataClass
Public Property ID As Int16
Public Property Description As String
Public Property Links As List(Of Integer)
End Class
Public Class PropertyCopier(Of TSource As Class, TDestination As Class)
Public Shared Sub Copy(ByVal Source As TSource, ByVal Destination As TDestination)
Dim SourceProperties = Source.[GetType]().GetProperties()
Dim DestinationProperties = Destination.[GetType]().GetProperties()
For Each SourceProperty In SourceProperties
For Each DestinationProperty In DestinationProperties
If SourceProperty.Name = DestinationProperty.Name AndAlso SourceProperty.PropertyType = DestinationProperty.PropertyType Then
If SourceProperty.PropertyType = GetType(Int16) Then
'1) Correctly detects the property type as a integer
ElseIf SourceProperty.PropertyType = GetType(List(Of)) Then
'2) Does not detect the List(of ) type
ElseIf SourceProperty.PropertyType.Name = GetType(List(Of)).Name Then
'3) Correctly detects the property type as List(of )
End If
If SourceProperty.CanWrite Then DestinationProperty.SetValue(Destination, SourceProperty.GetValue(Source))
Exit For
End If
Next
Next
End Sub
End Class
CodePudding user response:
The problem is that the generic type parameter of your object is set to something specific, e.g. you're comparing List(Of String)
to List(Of)
and they are not the same. What you need to do is get the generic type definition from that generic type, i.e.
ElseIf SourceProperty.PropertyType.IsGenericType AndAlso
SourceProperty.PropertyType.GetGenericTypeDefinition() Is GetType(List(Of)) Then
Notice the correct us of Is
rather than =
as well.
CodePudding user response:
Depending on what you need to work with, you may also want to look for interface compatibility rather than exact equality. You can do that using Type.IsAssignableFrom
. I have similar code that looks for a list like this:
If sourceProperty.PropertyType.IsGenericType _
AndAlso GetType(IList).IsAssignableFrom(sourceProperty.PropertyType) Then
'...
End If
(building upon the previous answer)
If you need it, you can get the generic type argument using e.g. sourceProperty.PropertyType.GenericTypeArguments(0)
.