Copying an Instance of a Class

Here, this is a bit better as I am assuming Cloning objects of various types is an exceptional situation, thus raising an exception.

Sub CloneInto(a As Object, b As Object)
  Dim aTi As Introspection.TypeInfo = Introspection.GetType(a)
  Dim bTi As Introspection.TypeInfo = Introspection.GetType(b)
  
  If aTi.FullName <> bTi.FullName Then
    Dim ex As New RuntimeException
    ex.Message = "a and b are of different types"
    Raise ex
  End If
  
  For Each p As Introspection.PropertyInfo In aTi.GetProperties
    p.Value(b) = p.Value(a)
  Next
End Sub

When you have a target, it’s not cloning, it’s copying. Clone is GetMeACopyFromYOU( you) or you.GetMeACopyFromYOU()

I didn’t want to go into the complexities of instantiating a class dynamically, thus I bent the rules a bit. Add a method to your class

Function Clone() As Person
  Dim other As New Person
  CloneInto(Self, other)
  // Copy things that require deep copy
  Return other
End Function

ok. :wink:

[quote=116479:@Jeremy Cowgar]Here is a simple CloneInto method. It, however, does a Shallow copy. I offer it here as something to build upon. Bear in mind, a global deep copy method can be very tricky. For example, say you have a Person object that has FirstName As String, LastName As String, Parents() As Person and Children() As Person… You can see how an improperly coded Deep clone could be cloning forever and ever :slight_smile:

Function CloneInto(a As Object, b As Object) As Boolean
  Dim aTi As Introspection.TypeInfo = Introspection.GetType(a)
  Dim bTi As Introspection.TypeInfo = Introspection.GetType(b)
  
  If aTi.FullName <> bTi.FullName Then
    Return False
  End If
  
  For Each p As Introspection.PropertyInfo In aTi.GetProperties
    p.Value(b) = p.Value(a)
  Next
  
  Return True
End Function

I will be doing a Webinar on Introspection at the end of August, it will include a more detailed version of the above method, but above is a place to start, bearing in mind it is a Shallow method.

Oh, a possible change to the above is to not make it return a Boolean, but make it raise an Exception if the types are different.[/quote]
Yes I have just come up with something similar and then stumbled upon the issue of some of my properties are classes themselves which I would then have to use introspection to iterate through their properties some of which are arrays of classes and now see the potentially infinite cavern opening up

It gets worse. Say you have a open TextInputStream… Should the deep copy create and open a brand new TextInputStream or should it simply reference the existing? If referencing the existing then if you do objA.CloseStream the stream on objB is now closed also. Now imagine it is a database connection… which happens to have an ErrorCode of 2938 at the time, you instantiate a new database connection, copy everything but error information, then try to call open on it (so you have a real “deep” copy) only to find out you have reached the maximum number of connections.

It gets real tricky on anything but basic types.

That’s why you should have an obj.clone() instead a clone(obj). Each class should treat its internal specifics.

I don’t understand how it would make a difference when it runs across a complex type?

It’s not just a copy, its knowing how internal states are. How should I treat a temporary open file created and open? Maybe the designer could opt for creating a new one and passing to the clone.

A generic function does not treat the internals. A method for each class treats its specifics.

OK, so you are speaking not so much about a.Clone(obj) vs. a.Clone() but you are speaking of a specific, hand written, intelligent .Clone() vs. a generically applicable, dumb .Clone().

I was speaking about a generic function clone(obj) against a class method obj.clone() handling the details.

You can have a dumb obj.clone() that would suffer the same fate, right? So the difference is a dumb vs. intelligent obj.clone(), not it’s parameters. You could have an intelligent obj.clone(other) that would do the right thing as well. It’s all about the brains vs. genericity.

a1 = clone(a)
b1 = clone(b) // could lead to a bad a1 or b1 due to some “internal detail” not treated

a1 = a.clone() // the designer cared about an specific condition that should render a problematic copy of a (like setting a flag)
b1 = b.clone() // the designer cared about an specific condition that should render a problematic copy of b (like closing a file)

[quote=116501:@Rick Araujo]a1 = clone(a)
b1 = clone(b) // could lead to a bad a1 or b1 due to some “internal detail” not treated

a1 = a.clone() // the designer cared about an specific condition that should render a problematic copy of a (like setting a flag)
b1 = b.clone() // the designer cared about an specific condition that should render a problematic copy of b (like closing a file)[/quote]

Right, it’s all about the designer caring, not about the parameters/nomenclature. BTW, a few languages call this Clone() while others call it Copy(). i.e. Dim b As Person = a.Copy() is the same as Dim b As Person = a.Clone(), just depends on the language you are using.

Simple objects are simple copies, yes. Dumb copies would work for those cases, but the best design is trying to maintain the methods in a transparent coherent way. Better have a .clone() for each class, used the same way, than having multiple functions like GenericClone(obj) and SpecialClone(obj) to treat one specific case, for example. When you design another class you treat its specifics there. If you forgot to create its .clone() the programmer will have a compiler error and you will need to create it. It avoid design errors. It avoids programmers using wrong cloning functions in specific objects.

It’s more about design/definition than nomenclature and parameters. Cloning returns a copy of an instance. Copying pushes a copy to a destination. Names are details of the language. It’s ok having a cloning method called obj.copy() and it could double as a copying method overloaded as obj.copy(destination) // Interesting design decision by the way.

Or just toss the whole mess of “Clone” out and create a copy constructor

[code]Class myClass
sub Constructor( constructFrom as myClass )
// whatever you need to do to copy one instance into another
end sub
end class

dim a as new myClass
dim b as new MyClass(a)[/code]

Works, but lost a bit in semantics. With Clone() you can do things like this in one line: Result = obj.Clone().DestructiveTest()

Thats really a glitch more than anything (in fact I think there’s a bug report about this)

Class myClass
        sub Constructor( )
        end sub

        sub Constructor( constructFrom as myClass )
              // whatever you need to do to copy one instance into another
        end sub

        Function DestructiveTest() As integer 
              // note you could make this test return any type
        end function
End Function

end class
  
  dim a as new myClass
  dim b as new MyClass(a)
  dim c as integer = MyClass( new myClass(b) ).DestructiveTest

Or if the destructive test were to return an instance

         dim c as myClass = MyClass( new myClass(b) ).DestructiveTest

Its a little more work BUT the idea of a “copy constructor” is whats being described regardless of the name
The entire conversation has been about “return a copy” - whether its deep or shallow but its still a copy