Search code examples
vb.netdesign-patternscircular-referenceicloneable

VB.Net clone hierarchy where parent has reference to child and vice versa -> circular reference


This is the situation:

Class A 
    Implements ICloneable

    Public Property Children As List(Of Child)

    Public Function Clone() As Object Implements ICloneable.Clone
        Return New A With {
            .Children = Children.Select(Function(c) DirectCast(c.Clone(), Child)).ToList()
        }
    End Function
End Class

Class Child 
    Implements ICloneable

    Public Property Parent As A

    Public Function Clone() As Object Implements ICloneable.Clone
        Return New Child With {
            .Parent = DirectCast(Parent.Clone(), A)
        }
    End Function
End Class

The actual object is more complex, having several levels. I'm not sure how to solve this because, at the moment, whenever you call Clone on the parent A class, you will end up with a circular reference.

How can I avoid this situation? Should I create my own Clone function and pass along a parameter?


Solution

  • The simplest solution is to just have the Child class not clone the Parent property at all. When a Child clones itself, it could either leave the Parent property the same, or just leave it null. For instance:

    Class Child 
        Implements ICloneable
    
        Public Property Parent as A
    
        Public Function Clone() As Object Implements ICloneable.Clone
            Return New Child() With { .Parent = Me.Parent }
        End Function
    End Class
    

    Then, when the parent A class clones itself, it could set the Parent property of all the cloned children, like this:

    Class A 
        Implements ICloneable
    
        Public Property Children As List(Of Child)
    
        Public Function Clone() As Object Implements ICloneable.Clone
            Return New A() With 
                {
                .Children = Me.Children.Select(
                    Function(c)
                        Dim result As Child = DirectCast(c.Clone(), Child))
                        result.Parent = Me
                        Return result
                    End Function).ToList()
                }
        End Function
    End Class
    

    Alternatively, as you suggested, you could make your own Clone method which takes the parent object as a parameter:

    Class Child 
        Public Property Parent as A
    
        Public Function Clone(parent As A) As Object
            Return New Child() With { .Parent = parent }
        End Function
    End Class
    

    It won't implement ICloneable, but as long as you don't need it to be interchangeable with other types of ICloneable objects, then that won't matter. Similarly, you could just overload your constructor:

    Class Child 
        Public Property Parent as A
    
        Public Sub New()
        End Sub
    
        Public Sub New(childToClone As Child, parent As A)
            ' Copy other properties from given child to clone
            Me.Parent = parent
        End Sub
    End Class