5

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
13
  • 2
    @GSerg: I am afraid but that is not correct. You set VB_PredeclaredId =True and then you can create a global default instance of your user-defined class. Question is: Why need the OP to find out if he is using a default instance of his own class. That seems strange to me. Either you know it when you write the code or ... Commented Oct 20, 2018 at 19:47
  • 1
    Damn newbies, can't they ask a question properly! Full class listing in edited Question... Commented Oct 21, 2018 at 5:08
  • 1
    So, do I get you rigt you would like to know within the class if it was called by the global default instance or if it was called by a variable you declared, right. I do not know if it's possible, sorry. But anyway, why would you need to know that? You write the code and you know if it's the default instance or not. Maybe you are looking for something like a constructor or factory. Commented Oct 21, 2018 at 6:07
  • 1
    @GSerg The question is valid, even if it presently has no justification: I'm looking for some intrinsic dynamic distinction between the global default and explicitly created instances. It occurred to me that Class_Initialize() might not be called for the global default, but my test shows it is (as does putting a breakpoint on that routine) Commented Oct 21, 2018 at 9:00
  • 1
    Is this a reasonable motivation: to use the global instance as a singleton. Program to fail if it discovers any explicitly created instance. Commented Oct 21, 2018 at 20:59

2 Answers 2

8

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
    
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, upvoted. Of course, you just have to compare the "addresses". Sometimes you don't see the wood for the trees :-)
@Storax You just have to look for the leaks in the abstractions. ;-)
@Storax and Comintern: speaking of abstraction level, a more "high level" code may be IsDefaultInstance = Me Is TestClass; of course, inside the class module
0

You can obtain the default instance by using the Class_Initialize and a Static Function within the class to store the default instance.

Using an example extract of my class clsCustomer which has it's VB_PredeclaredId = True

'Note Class_Initialize is called the first time the clsCustomer is accessed
'You can also do things like If Not Me Is clsCustomer for singleton classes i.e cannot create an instance other then default instance

Private Sub Class_Initialize()

    If Me Is clsCustomer Then
        GetDefaultInstance
    End If
End Sub


Static Function GetDefaultInstance() As clsCustomer

    Dim pvtDefaultInstance As clsCustomer
    If pvtDefaultInstance Is Nothing Then
        If Not Me Is Nothing Then
           Set pvtDefaultInstance = Me
       End If
    End If
    Set GetDefaultInstance = pvtDefaultInstance
End Function

In a module to test

Sub TestDefaultInstance()

    Dim pvtCustomer As clsCustomer
    Debug.Print ObjPtr(clsCustomer.GetDefaultInstance)
    Debug.Print ObjPtr(pvtCustomer)
    Set pvtCustomer = New clsCustomer
    Debug.Print ObjPtr(clsCustomer.GetDefaultInstance)
    Debug.Print ObjPtr(pvtCustomer)
    Debug.Print IsDefaultInstance(clsCustomer.GetDefaultInstance, pvtCustomer)
End Sub

Public Function IsDefaultInstance(byval defaultObject as Object, byval compareObject as Object) As Boolean

    Dim isDefault as Boolean
    if defaultObject is compareObject then
      isDefault = True
    End if
    IsDefaultInstance = isDefault 
End Function

Output:

 2401988144720    (The default instance)

 0                (The pvtCustomer instance not yet set and equal to nothing)

 2401988144720    (The default instance)

 2401988142160    (The new pvtCustomer instance which isn't the same as the default instance)

False   (False returned as the customer default object instance isn't the same as the new pvtCustomer object)

Notes: Output ObjPtr's will vary each run i.e they are memory references and only for example.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.