Class (Static) Methods in VBA

2019-01-21 07:14发布

问题:

I wonder, whether it is possible to create class-methods in VBA. By class-method I mean methods that can be called without having an object of the class. The 'static'-keyword does that trick in C++ and Java.

In the example below, I try to create a static factory method.

Example:

'Classmodule Person'
Option Explicit
Private m_name As String
Public Property Let name(name As String)
    m_name = name
End Property
Public Function sayHello() As String
    Debug.Print "Hi, I am " & m_name & "!"
End Function

'---How to make the following method static?---'
Public Function Create(name As String) As Person
    Dim p As New Person
    p.m_name = name
    Set Create = p
End Function

'Using Person'
Dim p As New Person
p.name = "Bob"
p.sayHello 'Works as expected'
Set p2 = Person.Create("Bob") 'Yields an error'

回答1:

That ("Public Shared") would only work in VB.Net.

There is no way to define Class Methods in VBA (or VB). I'd suggest to create a public function in a module.



回答2:

1. Create a normal class containing the public method(s) you need to be 'static'

2. Include a public method [in this 'static' class] that initialises the [private] 'static fields' within the class (it can take parameters if you wish)

3. Create a module acts as a factory

Public Function CreateStaticClass(parameters for 'constructor') As StaticClass

    Dim static As StaticClass
    Set static = New StaticClass
    Call StaticClass.Constructor(pass in parameters)
    Set CreateStaticClass = static

End Function

4. you can now use the 'static' class by calling CreateStaticClass('parameters').MethodName('parameters') there is no need to initialise the instance as that is done by the factory method

5. (Optional) If you want to enforce singleton instances you can create a module that acts as a singleton container - include a private instance variable and a public accessor property. optionally you can use a 'let' setter to allow the singleton to be 'replaced' with a new [static] class (using different constructor parameters - see #2,3). Use 'Let' for the setter, so you can assign the singleton without using 'set' ala OO languages

Private curStaticClass as StaticClass

Public Property Get CurrentStaticClass() As StaticClass 

    If curStaticClass Is Nothing Then Set curStaticClass = CreateStaticClass

    Set CurrentStaticClass = curStaticClass  

End Property

Public Property Let CurrentStaticClass(value As StaticClass)

    If Not (curStaticClass Is Nothing) Then Set curStaticClass = Nothing

    Set curStaticClass = value 

End Property

6. To assign the singleton:

CurrentStaticClass = CreateStaticClass(parameters)

7. To use the singleton:

[value = ] CurrentStaticClass.MethodName(parameters)


回答3:

You could try setting the VB_PredeclaredId attribute of the class you wish to be static to True. This creates a default instance of the class in much the same way that forms work in VBA (notice that you can refer to them directly without creating an instance. I know that this is not best practice but it is possible).

This means that you would have more of a singleton-style class, but it could serve your requirements...

You can't set this directly from the VBA IDE itself, however, you can perform the following steps:

1. Export the class you wish to make static to a folder.

2. Open the .cls file you exported in your favourite text editor and change the entry for VB_PredeclaredId so that it reads VB_PredeclaredId = True.

3. Save the file and re-import into VBA.

You should then be able to call your public methods on the class without having to instantiate the class. Bear in mind that the Initialize method is only called the first time you execute a class method/access a class property, and Terminate method is never called. Therefore you may wish to write your own constructor and also ensure you explicitly call the destructor if you need one.

Reference: UtterAccess.com Singleton Example

Reference: http://msdn.microsoft.com/en-us/library/ee199159.aspx



回答4:

Bit late in the day but what the heck

There are no class or static methods in VB6/VBA. But you can explicity state the name of a module. You can't have a module and a class of the same name but you could call it something similar.

So I could have a class called Employee and a module called EmployeeUtil and then I can write:

  Dim emp As Employee
  Dim code As String
  Set emp = EmployeeUtil.Create( "Smith", "John", 21-Feb-1988)
  code = "123XY"
  If EmployeeUtil.IsCodeValid( code) Then
    emp.Code = code
  Else
    emp.Code = EmployeeUtil.DefaultCode
  EndIf

Yes, the values are hard coded and the code handling should probably be under the property setter but that is not the point I'm trying to make. EmployeeUtil is essentially being a place holder for non-instance members.

You'll note that the Create method this way gives us a pseudo like constructor for the Employee class. All that function does is create an instance of Employee, assign the parameters via the property setters and then returns the instance. If your constructing instances of objects in a lot of places, then this can save a lot of code.



回答5:

AFAIK, the closest you can get (and it's not that close) is to use an "anonymous" instance, so something like this:

With New NotReallyStaticClass
    .PerformNotReallyStatic Method, OnSome, Values
End With


回答6:

While this is not strictly an answer to the question itself, I'd like to point out that Mike Woodhouse's solution should be avoided. Every time creating a new instance of an object is a performance hit and it really doesn't solve the original problem - it doesn't create a static object and it doesn't provide static methods either.

Since VBA has no concept of class functions, the closest one can get is using functions in modules.

As for factory methods, I suggest creating a module with the word Factory appended to the name of the class the module is creating. Something like:

'Module PersonFactory
Option Explicit

Public Function Create(ByVal sName As String) As Person

    'Code here

End Function

This is far from the static method concept of other languages but at least it provides a pattern that can be used in a project.



回答7:

Instancing property of a similar class is available for use in static classes. Instancing property for it 'GlobalMultUse' must specify.

Static class example:

' Error Class in ClassInstancing ActiveDLL project
Option Explicit

Private m_errorID As Integer
Private m_Description As String

Public Property Get ErrorID() As Integer
ErrorID = m_errorID
End Property

Public Property Let ErrorID(ByVal vNewValue As Integer)
m_errorID = vNewValue
End Property

Public Property Get Description() As string
    Description = m_Description
End Property

Public Property Let Description(ByVal vNewValue As string)
    m_Description = vNewValue
End Property

Public Function Error() As Error
    Dim errorInstance As New ClassInstancing.Error

    With errorInstance
        .ErrorID = Me.ErrorID
        .Description = Me.Description
    End With

    Set Error = errorInstance
End Function

Public Sub RaiseError(ByVal pErrorID As Integer, ByVal errorSource As String, ByVal errorDesc As String)
Err.Raise pErrorID, errorSource, errorDesc
End Sub

Public Sub ShowError()
   MsgBox "Error ID: " & CStr(Me.ErrorID) & vbCrLf & _
    "Desc: " & Me.Description
End Sub

GlobalMultiUse Instancing property to specify the class as a set of...

Sample usage this global (static!) class in other standart EXE project:

Private Sub Command1_Click()

    ClassInstancing.Description = "Sample-1 error using !"
    ClassInstancing.ErrorID = 9990

    'Dim multiuseClass As ClassInstancing.Error
    'Set multiuseClass = ClassInstancing.Error

    MsgBox ClassInstancing.Error.ErrorID & vbCrLf & ClassInstancing.Error.Description, vbInformation, "Sample Usage 1"

    ClassInstancing.Description = "Sample-2 error using !"
    ClassInstancing.ErrorID = 1110

    ClassInstancing.ShowError
End Sub

Finally, notes in MSDN ((MSDN Library Visual Studio 6.0, 'Instancing Property')):

GlobalMultiUse. Similar to MultiUse, with one addition: properties and methods of the class can be invoked as if they were simply global functions. It’s not necessary to explicitly create an instance of the class first, because one will automatically be created.



回答8:

you have to declare p2 before you can use the Set as follows:

dim p2 as Person

Once you do this, you have to replace the Set statement using a standard assignment: p2 = Person.Create("Bob")

In the Function: remove the "Set" key word...this could also be the source of an error.

I'm flying blind, but logically it seems like this should work. I am new to using Class modules in VBA but they aren't too different from using VB.Net properties.



标签: vba static