Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need the Me keyword in class modules?

Tags:

oop

class

excel

vba

These two subs do the same thing when inside a class.

Sub DemoMe( )
    Me.AboutMe    ' Calls AboutMe procedure.
End Sub

Sub DemoMe( )
    AboutMe    ' Does the same thing.
End Sub

What is the point? Does the Me keyword do anything? What is the preferred way of an object accessing its own members?

like image 534
PBeezy Avatar asked Feb 26 '19 14:02

PBeezy


People also ask

What is the use of me keyword?

The Me keyword behaves like an implicitly declared variable. It's automatically available to every procedure in a class module. When a class can have more than one instance, Me provides a way to refer to the specific instance of the class where the code is executing.

What do you use class modules for?

Use a class module to create a definition for a custom object. The name with which you save the class module becomes the name of your custom object. Public Sub and Function procedures that you define within a class module become custom methods of the object.

What is class module in MS Access?

A class module is a group of code that allows you to create objects and utililise them in your application.


2 Answers

tldr; No, although there are situations where it can be useful.


From the VBA language specification (5.3.1.5):

Each procedure that is a method has an implicit ByVal parameter called the current object that corresponds to the target object of an invocation of the method. The current object acts as an anonymous local variable with procedure extent and whose declared type is the class name of the class module containing the method declaration. For the duration of an activation of the method the data value of the current object variable is target object of the procedure invocation that created that activation. The current object is accessed using the Me keyword within the <procedure-body> of the method but cannot be assigned to or otherwise modified.

That's all it is, just a "free" local variable that refers to the specific instance that the method is being called on. This also happens to be the default context for the procedures during their invocation, so it can be omitted if the code is intended to operate on the current instance. Although as @HansPassant points out in the comment above, it also allows the editor to bind to the interface and provide IntelliSense.

That said, there are a couple instances where you would either want to or have to use it (this is by no means an exhaustive list):


Naming collisions:

If your class has a member that "hides" a built-in VBA function, it can be used to make the scope explicit:

Public Property Get Left() As Long
    '...
End Property

Public Property Get Right() As Long
    '...
End Property

Public Property Get Width() As Long
    Width = Me.Right - Me.Left
End Property

Equity Checks:

Public Function Equals(other As Object) As Boolean
    If other Is Me Then
        Equals = True
        Exit Function
    End If
    '...
End Function

Fluent Functions:

This can be a useful pattern for compositing objects - you perform an action, then return the instance of the class so they can be "chained". Excel's Range interface does this in a lot of cases:

Public Function Add(Value As Long) As Class1
    'Do whatever.
    Set Add = Me
End Function

Public Sub Foo()
    Dim bar As New Class1
    bar.Add(1).Add(1).Add 1
End Sub
like image 116
Comintern Avatar answered Oct 06 '22 07:10

Comintern


Not any more than there are reasons to use this in Java, C#, or any other language: it's a reserved identifier that represents the current instance of the class - what you do with that is up to your imagination.

What is the preferred way of an object accessing its own members?

Indeed, an object doesn't need the Me keyword to access it own public interface. Same as this in other languages, I'd even call it redundant. However it can sometimes be a good idea to explicitly qualify member calls with Me, especially when the class has a VB_PredeclaredId attribute (e.g. any UserForm): referring to UserForm1 in the code-behind of UserForm1 yields a reference to the default instance of the class, whereas qualifying member calls with Me yields a reference to the current instance of that class.

Accessing Inherited Members

VBA user code can't do class inheritance, but a lot of VBA classes do have a base class. The members of UserForm when you're in the code-behind of UserForm1, and those of Worksheet when you're in the code-behind of Sheet1, aren't necessarily easy to find. But since the inherited members show up in IntelliSense/auto-complete, you can type Me. and browse a list of members inherited from the base class, members that you would otherwise need to know about in order to invoke.

A class creating an instance of itself inside itself? That I've never seen.

You're missing out! I do this all the time, to enable referring to the object instance held by a With block, inside a Factory Method - like this GridCoord class.

Public Function Create(ByVal xPosition As Long, ByVal yPosition As Long) As IGridCoord
    With New GridCoord
        .X = xPosition
        .Y = yPosition
        Set Create = .Self
    End With
End Function

Public Property Get Self() As IGridCoord
    Set Self = Me
End Property

Note that while the GridCoord class exposes a getter and a setter for both X and Y properties, the IGridCoord interface only exposes the getters. As a result, code written against the IGridCoord interface is effectively working with read-only properties.

Another use is to get the name of the class module, without needing to hard-code it. This is particularly useful when raising custom errors: just use TypeName(Me) for the Source of the error.


The Builder Pattern notoriously returns Me, which enables a "fluent API" design that makes it possible to write code that incrementally builds complex objects through chained member calls, where each member returns Me (except the final Build call, which returns the type of the class being built):

Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
    .WithFoo(42) _
    .WithBar("test") _
    .WithSomething _
    .WithSomethingElse
    .Build
like image 21
Mathieu Guindon Avatar answered Oct 06 '22 08:10

Mathieu Guindon