I was wondering if there's a way in which I can share variables between instances of separate class modules?
I have two classes:
Class 1
Class 2
Inside class 1, I have multiple global variables which I would like Class 2 to have access to once instantiated.
I could use get and set properties for each of the variables but I have about 40/50 so it just seems a bit tedious.
So, instead, I'm trying to pass the current instance of Class 1 to Class 2 using set property.
I've created a minimal example to illustrate my current efforts:
Class 1:
Public test As String
Private Sub Class_Initialize()
Call setTest
Dim b As Class2
Set b = New Class2
End Sub
Public Property Set Classed(ByRef vClass As Class1)
Set vClass = Me
End Property
Public Sub setTest(t As String)
test = "Sam"
End Sub
Class 2:
Private Sub Class_Initialize()
Dim newClass As Class1
newClass.Classed = newClass
' Want to be able to access the test String from class 1
End Sub
Obviously what I am doing at the moment is incorrect, so am wondering if someone could point out where I'm going wrong and show me how to achieve this class sharing?
Just to add: when running the code, I receive a compile error at line: newClass.Classed = newClass. Error: Invalid use of property
Not too sure but I sense a bit of a Circular Reference in your example?
What Are Circular References?
A circular reference occurs when two objects hold references to each other.
You could try an alternative by exposing a Dictionary object through your class, where the Key will be your "variable name", and the Value will hold the actual value.
An example could be:
Class1
Option Explicit
Private mList As Object
Public Property Get List() As Object
Set List = mList
End Property
Private Sub Class_Initialize()
Set mList = CreateObject("Scripting.Dictionary")
End Sub
Private Sub Class_Terminate()
Set mList = Nothing
End Sub
Implementation:
Sub ClassTest()
Dim a As Class1
Set a = New Class1
Dim b As Class1
Set b = New Class1
a.List("VarName") = "Sam" 'Set
b.List("VarName") = a.List("VarName") 'Get / Set
Debug.Print b.List("VarName") 'Get
Set a = Nothing
Set b = Nothing
End Sub
'Output
'Sam
Related
I am confused. I am new to VBA classes. I want to add multiple methods to a property of a class, or add properties to another property. I may not have the terminology correct?
I can add one property, but I want to drill down deeper.
For instance if I make a class person:
PersonClass.Features.Hair.Texture.Color
PersonClass.Features.Hair.Texture.Style
PersonClass.Features.Hair.Length
I am not sure how to go about this.
e.g.
MyClass.MyProperty.MyMethod1
MyClass.MyProperty.MyMethod2
MyClass.MyProperty.MyMethod3
or
MyClass.MyProperty.MyMethod1.MyMethod2
Here is an example to illustrate the concepts mentioned in the comments:
Main Form
Option Explicit
Private Sub Form_Load()
Dim p As Person
Set p = New Person
p.Features.Hair = "Red"
MsgBox p.Features.Hair
End Sub
Person Class
Option Explicit
Private m_Features As Features
Private Sub Class_Initialize()
Set m_Features = New Features
End Sub
Public Property Get Features() As Features
Set Features = m_Features
End Property
Features Class
Option Explicit
Private m_Hair As String 'this would actually be another class
'in your example
Public Property Get Hair() As String
Hair = m_Hair
End Property
Public Property Let Hair(ByVal Value As String)
m_Hair = Value
End Property
Sub Main()
Dim test As New Class1
End sub
Class1:
Private Sub Class_Initialize()
msgbox(Name_of_Class_Instance)
End Sub
I want the msgbox to show "Test"
Can that be done in VBA?
As per my understanding, you are trying to get the class instance name which is declared in module.
First of all you need to do is create a class like below.
Public classname As String
'Below method is going to get the value from module.
Public Sub Class_Initialize()
MsgBox classname
End Sub
Second, create a module like below.
Sub getname()
Dim test As Class1
Set test = New Class1
'Here we are passing the class name as 'test' and executing the 'Class_Initialize' method
With test
.classname = "test"
.Class_Initialize
End With
End Sub
Now you will get the instance name of the class.
Quite simple; I've made a class with a method that's public since another class calls this method.
I would like the method only to show up on intellisense in the 2nd class which holds some special reference to the 1st one. Any other class or module should not be able to see the method.
Something along the lines of
Semi-Private (except for Class2) Sub ...
in the method of Class1, or in Class2
Can See Semi-Private methods of Class1
Instead of getting complicated with it, and using a roundabout approach, just use the tools meant for the job:
Class Module IFoo
Public Sub Bar()
' Interface methods are empty
End Sub
Class Module Foo
Private Type TFoo
Baz As String
End Type
Private this As TFoo
Implements IFoo
Private Sub Class_Initialize()
this.Baz = "I am a class that implements IFoo."
End Sub
Private Sub IFoo_Bar()
' Do Something
Debug.Print this.Baz
End Sub
Module Baz
Sub RunBaz()
Dim MyFoo As IFoo
' Note that this WILL NOT work. Nothing happens.
Set MyFoo = New IFoo
Debug.Print MyFoo.Bar
' Set MyFoo to be equal to a Foo (which implements IFoo)
Set MyFoo = New Foo
Debug.Print MyFoo.Baz
End Sub
This makes the methods only visible when the methods are being accessed through an interface which makes them public. Therefore, in order to use the methods in Foo we must first create an instance of Foo using an IFoo variable type.
Then, it is as simple as creating a new class which creates a Foo from an IFoo for its own use.
Class Module IImportantWorker
Public Sub DoSomethingImportant()
End Sub
Class Module ImportantWorker
Private Type TImportantWorker
Implementation As IFoo
End Type
Private this As TImportantWorker
Implements IImportantWorker
Private Sub Class_Initialize()
Set this.Implementation = New Foo
End Sub
Public Sub IImportantWorker_DoSomethingImportant()
this.Implementation.Bar
End Sub
You could get fancy from here and make a property of Foo that is exposed by IFoo that tells it whether or not it can work. This would have to be exposed through the IFoo interface (or, alternatively a separate interface so that the two interfaces must be used in conjunction).
Without locking the class (which I wouldnt recommend anyways, it seems foolish and pointless) Foo will still allow Bar if it is created as a IFoo. But if you just make Foo = New Foo then Foo will do nothing (or rather, expose nothing).
For additional resources, I highly recommend reading these excellent posts that go into greater depth about the processes:
Is VBA an OOP language, and does it support polymorphism?
https://rubberduckvba.wordpress.com/2016/06/16/oop-vba-pt-1-debunking-stuff/
https://rubberduckvba.wordpress.com/2016/07/05/oop-vba-pt-2-factories-and-cheap-hotels/
You may create the class that contains all properties and methods, name it BigClass:
Option Explicit
Dim i1 As Integer
Dim i2 As Integer
Dim i3 As Integer
Property Let var1(v As Integer)
i1 = v
End Property
Property Get var1() As Integer
var1 = i1
End Property
Property Let var2(v As Integer)
i2 = v
End Property
Property Get var2() As Integer
var2 = i2
End Property
Property Let var3(v As Integer)
i3 = v
End Property
Property Get var3() As Integer
var3 = i3
End Property
Then you can make classes that inherits BigClass methods, but not necessary all of them. For example, SmallClass1 that will use only var3 variable.
Option Explicit
Dim BigOne As BigClass
Private Sub Class_Initialize()
Set BigOne = New BigClass
End Sub
Private Sub Class_Terminate()
Set BigOne = Nothing
End Sub
Property Let var3(v As Integer)
BigOne.var3 = v
End Property
Property Get var3() As Integer
var3 = BigOne.var3
End Property
Then you can create SmallClass2 that use var2 and var1 but not var3. You get the point.
An clear example would be involved and lengthy and I have deposited one on my blog here VBA - Difference between Friend and Public with inter project references.
Essentially you use the Friend keyword but nothing happens until you split your code across workbooks which itself brings issues such as Instancing and factory functions. Example given should work though. Enjoy!
Say I have an object, Email, one of whose properties is an object called EmailSkinner.
The EmailSkinner is instantiated in the class_initialize subroutine like this.
private sub class_initialize()
set EmailSkinner = new MyEmailSkinner
end sub
Must I explicitly set the EmailSkinner object to nothing in the class_terminate subroutine of Email?
private sub class_terminate()
set EmailSkinner = nothing
end sub
Or does this happen automatically when I set the Email object itself to nothing?
This is an interesting question. Your assumption is correct any object's you instantiate inside the scope of the parent class will be released when the parent class is released from memory.
However as with all object instantiation in VBScript (and by extension Classic ASP) there is nothing wrong with explicitly releasing objects using the Class_Terminate event.
Remember though that "scope" is important here.
If your EmailSkinner object reference is declared outside of the parent class (regardless of whether it is instantiated inside the class) the reference will remain and will require Class_Terminate() to force the object reference to be released.
Examples
Object Reference is declared inside Class scope.
Class ParentObject
Private _ChildObject
Private Sub Class_Initialize()
Set _Object = new ChildObject()
End Sub
End Class
Object Reference is declared outside Class scope (wouldn't recommend this approach).
Dim GlobalObject
Class ParentObject
Private Sub Class_Initialize()
Set GlobalObject = new ChildObject()
End Sub
'GlobalObject reference will remain so we need to
'force it to be released.
Private Sub Class_Terminate()
Set GlobalObject = Nothing
End Sub
End Class
By default, Class objects are auto destroyed, but if you create new objects outside, you will need to release them from memory .
Is always recommended that we clean memory in all scenarios .
I made a small piece of code for you to test ( I hope this would be similar to what you are trying to explain, since you didn't show us your code ) .
This code help us to check if something remains in memory after some steps of execution and declaration ( just take out the apostrophes at bottom to test the code ) :
Class EmailSkinner
public color
public size
Private Sub Class_Initialize
color = "blue"
size = 300
End Sub
End Class
Class Email
public details
public name
Private Sub Class_Initialize
Set details = New EmailSkinner '//Module Scope
End Sub
Private Sub Class_Terminate
Set details = Nothing
End Sub
End Class
Set email1 = New Email '//Global Scope
With email1
.details.color = "black"
.details.size = 400
End With
''//Take out the apostrophe to test one of the next lines
'Response.Write email1.details.color '//ASP only
'wscript.echo email1.details.color '//Wscript only
Set email1 = Nothing
New to building class modules in VBA and struggling with populating one object which is defined within another object.
For instance, I have two class modules, Class1 and Class2
Class1
Dim mobj As Class2
Public Property Set SetObj(obj As Class2)
Set mobj = obj
End Property
Class2
Public FirstName As String
These are accessed from a standard module as follows:
Sub test()
Dim X As Class1
Set X = New Class1
Set X.SetObj = New Class2
X.SetObj.FirstName = "Bruce"
End Sub
This however fails in X.SetObj.FirstName = "Bruce" when i get an "Invalid use of property message". Any assistance would be greatly appreciated.
You are close.
Class1:
Private mobj As Class2
Public Property Set Obj(Obj As Class2)
Set mobj = Obj
End Property
Public Property Get Obj()
Set Obj = mobj
End Property
Sub (the X.Obj.FirstName line calls the Get property, not the Set property):
Sub test()
Dim X As Class1
Set X = New Class1
Set X.Obj = New Class2
X.Obj.FirstName = "Bruce"
End Sub