I’m working on a project that requires me to make multiple copies of a custom object. That generally means I make a Duplicate method and assign each property of the source instance to the copy instance. It’s not terrible, but I just have to be careful that when I change the properties of the class that I update the Duplicate method. I decided that I would see what other options are available and I found this StackOverflow answer that I think is interesting. So I tried it.
Instead of declaring a bunch of private variables in the class and all the getters and setters for those variables, all of the property values will be in a user-defined type. The UDT variable will be private to the class, but the actual UDT has to be in a standard module because VBA won’t let you put types in a class module. Now when I want to clone that class instance, I can pass the whole UDT variable. Unlike a class that passes the reference, the UDT makes a copy of all of the values in memory so you end up with two separate structures in memory – just what I want for a clone.
I made a CContact class that will have a FirstName, LastName, and LastContact property. The poorly named LastContact property is the date last contacted. I wanted to put at least two different data types (String and Date) in the class for demonstration purposes. The declaration of CContact looks like this:
Private mlContactID As Long
Private mtContactMemento As ContactMemento
I only have two private variables; ContactID that never want to copy and a ContactMomento variable that contains all the data that I do want to copy. I keep the ContactID getter and setter the same as I normally would, but the mtContactMomento property statements are a little different. First, let’s look at the ContactMomento type. This is in my MGlobals standard module.
Public Type ContactMemento
FirstName As String
LastName As String
LastContact As Date
I named the type ObjectName + Memento. Memento is from the Memento pattern, which is typically used to store a previous state for Undo purposes, although we’re not really using it like that here. The UDT holds all of the properties that I want to copy to a new class instance. Back in the class, the typical getters and setters look like this:
Public Property Let FirstName(ByVal sFirstName As String)
mtContactMemento.FirstName = sFirstName
Public Property Get FirstName() As String
FirstName = mtContactMemento.FirstName
That’s pretty much the same as typical property statements except I’m pulling the data from my private ContactMemento variable instead of individual variables for each project. With that all set up, this works just like a class that I would create normally – not using a UDT. The last bit of setup that I need is property statements for the ContactMemento as a whole.
Public Property Let ContactMemento(tContactMemento As ContactMemento)
mtContactMemento = tContactMemento
Public Property Get ContactMemento() As ContactMemento
ContactMemento = mtContactMemento
And then I need a way to clone the class, so I create a Clone property.
Public Property Get Clone() As CContact
Dim clsReturn As CContact
Set clsReturn = New CContact
clsReturn.ContactMemento = Me.ContactMemento
Set Clone = clsReturn
By assigning the ContactMemento from the source instance to the class instance, all the properties I want to copy are done so in one shot rather than one at a time. If I add any properties to the class, I don’t have to adjust the Clone property, which means that I won’t forget and miss a property. Let’s test it out. If create one instance, then clone it and change the LastContact property just for some variety.
Dim clsContact As CContact
Set gclsContacts = New CContacts
Set clsContact = New CContact
.FirstName = "Dick"
.LastName = "Kusleika"
.LastContact = Date
Dim clsClone As CContact
Dim clsSource As CContact
Dim clsContact As CContact
Set clsSource = gclsContacts.Contact(1)
Set clsClone = clsSource.Clone
clsClone.LastContact = Date + 1
For Each clsContact In gclsContacts
Debug.Print .ContactID, .FirstName, .LastName, .LastContact
I like a system that doesn’t rely on me being careful to update a Clone or Duplicate method. But there are some things I don’t like too. With this method, my class is not longer self-contained. The UDT declaration has to live in a standard module. If I copy my class module to another project, it won’t compile until I declare the UDT in the new project. I would also have to change all of my code generation stuff to use a UDT rather than individual private variables. In all the code I’ve written, I’ve probably only had to clone class instances about a half dozen times, so it’s probably not worth it.
You can download CloneClass.zip