Reputation: 23
I'll try to keep it simple, but this is making me almost rip my hair out as I do not understand why a certain property is being changed on one of my objects when I am not assigning it a change.
Sample class:
Public Class Person
Public Name As String
Public Age as UInteger
End Class
OK cool. In my project:
Dim Me as New Person, You as New Person
Me.Name = "John"
You.Name = "Terry"
Me = You
You.Age = 32
So I assigned the Name properties to 'Me' and 'You' respectively as John & Terry. I then coped the properties of 'You' To 'Me'.
When I go change the Age Property of 'You' to 32. The property age of 'Me' ALSO gets changed to 32 even though I never assigned it a change.
A total head scratcher as I am not quite catching the error here?
Upvotes: 2
Views: 533
Reputation: 54487
If you want to create a new object with the same property values then you have no option but to create a new object and copy the property values. There are a number of ways that you can implement the details though.
The most obvious and most basic is to simply create a new object and copy the property values:
Public Class Person
Public Property Name As String
Public Property Age As UInteger
End Class
Dim firstPerson As New Person
firstPerson.Name = "John"
firstPerson.Age = 32
Dim secondPerson As New Person
secondPerson.Name = firstPerson.Name
secondPerson.Age = firstPerson.Age
The next option to consider is to build the functionality into the type itself. If you do this, the first option is to implement the ICloneable
interface and use the MemberwiseClone
method:
Public Class Person
Implements ICloneable
Public Property Name As String
Public Property Age As UInteger
Public Function Clone() As Object Implements ICloneable.Clone
Return MemberwiseClone()
End Function
End Class
Dim firstPerson As New Person
firstPerson.Name = "John"
firstPerson.Age = 32
Dim secondPerson = DirectCast(firstPerson.Clone(), Person)
The ICloneable.Clone
method returns an Object
reference, because it must work for any type, so it's not ideal. You might choose not to implement the interface and change the return type, or you could add your own Copy
method and cast the result:
Public Class Person
Implements ICloneable
Public Property Name As String
Public Property Age As UInteger
Public Function Clone() As Object Implements ICloneable.Clone
Return MemberwiseClone()
End Function
Public Function Copy() As Person
Return DirectCast(Clone(), Person)
End Function
End Class
Dim firstPerson As New Person
firstPerson.Name = "John"
firstPerson.Age = 32
Dim secondPerson = firstPerson.Copy()
That MemberwiseClone
method creates a shallow copy of the current object, which means a direct copy of property value. That's fine for simple types like String
and numeric types but may not be for more complex types. For instance, if you had a property that referred to a DataSet
then the same property in the new object would refer to that same DataSet
object, not a new DataSet
containing the same data. The same goes for arrays and collections. If you don't want a shallow copy then you need to write your own code to explicitly create a deep copy in the specific way that you want, e.g.
Public Class Person
Public Property Name As String
Public Property Age As UInteger
Public ReadOnly Property Children As New List(Of Person)
Public Function Copy() As Person
Dim newPerson As New Person With {.Name = Name,
.Age = Age}
For Each child As Person In Children
newPerson.Children.Add(child.Copy())
Next
Return newPerson
End Function
End Class
Dim firstPerson As New Person
firstPerson.Name = "John"
firstPerson.Age = 32
firstPerson.Children.Add(New Person With {.Name = "Jim", .Age = 10})
firstPerson.Children.Add(New Person With {.Name = "Jane", .Age = 12})
Dim secondPerson = firstPerson.Copy()
The reason that's not done by default is that you could end up with a stack overflow. In this case, creating a copy of a Person
object will also create a copy of the Person
objects in its Children
collection. If there is a circular reference there somewhere, i.e. two Person
objects were in each other's Children
collection then you would just keep creating copies until the system ran out of resources. You need to be very careful when creating deep copies in order to avoid such situations. You need to be very sure that you only go to the depth you need and that you catch any circular references and stop copying when you get back to the start of the chain.
Notice that this last example does require you to specify each property explicitly. It is possible to avoid this if you use Reflection but that is less efficient and, unless you have a ridiculous number of properties, a bit silly as the few minutes it takes to type out those properties should be a very minor annoyance.
Upvotes: 3