2

I've prepared a standard module, MyPath, which contains a few useful functions. In this case standard module is better for me than a class module.

Much more comfortable to write:

Dim ext As String
ext = MyPath.getFileExtension("test.docx")   ' returns "docx"

Instead of:

Dim Pth As MyPath
Set Pth = New MyPath

Dim ext As String
ext = Pth.getFileExtension("test.docx")

or something like

Dim ext As String
With New MyPath
    ext = .getFileExtension("test.docx")
End With

The only problem with my "pseudo-static-class" module is its scope (and IntelliSense use).

Dim ext As String
ext = getFileExtension("test.docx")   ' This works, but I don't want it to

What I would like to achieve is:

  • If one doesn't specify MyPath when calling a function, IntelliSense does not suggest the methods from the module and they cannot be used.
  • In order to use any methods from the MyPath module, one needs to type in MyPath., and after the dot IntelliSense should suggest methods from the module.

I've tried some combinations with Option Private Module, Private functions, but no one works the way I described.
I am preparing a lot of classes and similar modules and I would keep everything in order.

edit
In short, I would like to force the compiler to throw an error if I don't specify the "parent" module for the function or sub. At the same time, if I type the name of my module I would be able to use IntelliSense (no solutions with run/call, just simply type module name, dot and select the member).

enter image description here

GSerg
  • 76,472
  • 17
  • 159
  • 346
Rafał B.
  • 487
  • 1
  • 3
  • 19
  • 1
    Are you talking about Excel modules in the same Workbook? – FaneDuru Aug 20 '20 at 13:02
  • If the functions are indeed in a standard module, as opposed to a class module, and if they are public, then by default they are accessible to other code without the need to type in the module name. If you are trying to use these functions from another document, then obviously you need to add a reference to the containing document first. – GSerg Aug 20 '20 at 13:04
  • Same workbook. So unfortunatelly it seems it isn't possible to hide module's functions, subs and show them only if it's name used :( I would avoid things became messy – Rafał B. Aug 20 '20 at 13:07
  • 2
    I am not sure I can get you... Do you like using intellisense, or you want hiding them from being accessed? Or both...? Do you want to not access some modules (functions), from other modules in the same workbook? Do you need it, or only play in order to see what's happening? – FaneDuru Aug 20 '20 at 13:14
  • @FaneDuru In short: In another module if I want use function from MyPath module, i must type name "MyPath." and then I should be able to choose my function. If I not type "MyPath" compiler should throw an error. – Rafał B. Aug 20 '20 at 13:28
  • No, you cannot do that. – GSerg Aug 20 '20 at 13:29
  • @gserg I found one way: create another module with the same function name. In the case "MyPath" module is not specified the compiler throws error "Ambiguous name detected". And it is working if I use reference to module (compiler knows which one function I want ti use). But it is not elegant solution... – Rafał B. Aug 20 '20 at 13:31
  • @GSerg it is possible with a Predeclared Class – ArcherBird Aug 20 '20 at 13:38
  • What? When compiler will throw an error? When you start writing a function name? Can you better explain what you want achieving? If you start typing something, would you like intellisense to search between all existing functions and make suggestions? I this what you are asking for? – FaneDuru Aug 20 '20 at 13:40
  • "I would like to force the compiler to throw an error if I don't specify the "parent" module "... When would you like this happening? If you start typing 'a' and no module name starting with 'a' exists? If yes, I do not think that it is possible. If not, can you better explain **in which moment the compiler must throw that error**? Use an example, please... – FaneDuru Aug 20 '20 at 13:46
  • 1
    @FaneDuru of course not when typing, it should stop me at compile time. Nevermind, ArcherBird gave solution, thank for the help for everyone here. – Rafał B. Aug 20 '20 at 13:51

2 Answers2

2

Another solution, inspired by VBA's Err object, is to implement a function that returns a reference to your "static object"

Public Static Function MyPath() As PathObject
    Dim result As PathObject
    If result Is Nothing Then Set result = New PathObject
    Set MyPath = result
End Function

Where PathObject is the class that contains all your pseudo static methods.

This approach has the added bonus that the caller cannot set their MyPath reference to Nothing - in case you decide your Static module should be stateful (perhaps for some costly initialisation routine)

PS I think the Static Function syntax is cute, but you could equally just declare result with the static keyword


In fact you could possibly make it even more concise with

Public Static Function MyPath() As PathObject
    Dim result As New PathObject
    Set MyPath = result
End Function

Not tested, but should have the behaviour that MyPath is instantiated only when used for the first time, but thereafter hangs around indefinitely

Greedo
  • 4,967
  • 2
  • 30
  • 78
  • I didn't even know `static` was a keyword you could apply to functions in vba – ArcherBird Sep 09 '20 at 18:06
  • 1
    @ArcherBird TBH it's nothing special - normally a Static function is one belonging to a Class but which doesn't require a class instance to be called. However in VBA it just means all local variables are declared as static (so basically the same as replacing Dim with Static in the function body) – Greedo Sep 09 '20 at 18:10
1

The behavior you describe is actually achievable via a Predeclared Class, but will require a little prep work.

To make a predeclared class, you can write up a class with whatever functions you want. Then export it to text (.cls) and edit it, setting the Attribute VB_PredeclaredId to True. Then re-import it. My Class is called PredeclaredClass and looks like this in the .cls file:

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "PredeclaredClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_Ext_KEY = "Rubberduck" ,"Predeclared Class Module"

Option Explicit

Public Sub ThisIsVisible()

End Sub

After you import this class, you can now use its methods only by referencing the class module (and no new instance needed).

enter image description here

You will not see it in intellisense nor be able to use its function without the module reference. The compiler will complain, provided you are using Option Explicit

enter image description here

By the way, Rubberduck makes this pretty easy to do this without the need to export/import modules.

ArcherBird
  • 2,019
  • 10
  • 35
  • Note that this creates an implicit "cached" instance of the class. Because all the methods are in fact instance methods now, they all will receive the implicit `this` argument, and hence will be ineligible for the `AddressOf` operator should you need it. – GSerg Aug 20 '20 at 13:43
  • 1
    It is AWESOME I was looking for something like this! Thanks a lot! Even better our favourite VBE addin helps with the process – Rafał B. Aug 20 '20 at 13:47
  • @GSerg You can still use something like [this](https://stackoverflow.com/questions/65507735/address-of-class-method-crash-on-x64) to get the address of the class methods. Could you please have a look at the linked question? It would be great if you could help. – Cristian Buse Dec 30 '20 at 14:48