Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a VBA object instance tell if it is the default instance?

Tags:

vba

This doesn't work:


clsTestDefaultInstance

Dim HowAmIInitialised As Integer

Private Sub Class_Initialize()
HowAmIInitialised = 99
End Sub

Public Sub CallMe()
  Debug.Print "HowAmIInitialised=" & HowAmIInitialised
End Sub

i.e clsTestDefaultInstance.CallMe() outputs HowAmIInitialised=99 because Class_Initialize() is called even for the default instance.

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "clsTestDefaultInstance"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Compare Database
Option Explicit

' test how class instance can tell if it is default
'clsTestDefaultInstance

Dim HowAmIInitialised As Integer

Private Sub Class_Initialize()
  HowAmIInitialised = HowAmIInitialised + 1
End Sub

Public Sub CallMe()
  Debug.Print "HowAmIInitialised=" & HowAmIInitialised
End Sub
like image 354
Martyn Barry Avatar asked Mar 06 '23 05:03

Martyn Barry


1 Answers

This is really, really simple... just compare the object pointer of the instance to the object pointer of the default instance:

'TestClass.cls (VB_PredeclaredId = True)
Option Explicit

Public Property Get IsDefaultInstance() As Boolean
    IsDefaultInstance = ObjPtr(TestClass) = ObjPtr(Me)
End Property

Testing code shows that it works just fine:

Private Sub TestDefaultInstance()
    Dim foo(9) As TestClass

    Dim idx As Long
    For idx = LBound(foo) To UBound(foo)
        If idx = 5 Then
            Set foo(idx) = TestClass
        Else
            Set foo(idx) = New TestClass
        End If
    Next

    For idx = LBound(foo) To UBound(foo)
        Debug.Print idx & foo(idx).IsDefaultInstance
    Next
End Sub

With that said, note that this comes with a couple caveats:

  • It pretty much guarantees that the default instance will be reinstantiated if you check to see if any instance is the default instance, because as you probably know, simply referencing the default instance will new it back up if it isn't already instantiated.
  • The default instance can change if you Unload it (for UserForm's) or set it to Nothing and then cause it to auto-instantiate again. It's best to think of VB_PredeclaredId as kind of like a contract that you will always get an instance back if you use the class name directly. That contract does not guarantee that it will always be the same one. Adding the following code to the bottom of the TestDefaultInstance procedure above will demonstrate:

    'This doesn't effect anything that stored a reference to it.
    Set TestClass = Nothing
    'Make a call on the default to force it to reinstantiate.
    Debug.Print TestClass.IsDefaultInstance
    'This will now be false.
    Debug.Print foo(5).IsDefaultInstance
    
like image 191
Comintern Avatar answered Jun 02 '23 14:06

Comintern