我需要在类模块中使用 Me 关键字吗?

PBe*_*ezy 5 oop excel vba class

这两个 sub 在一个类中做同样的事情。

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

Sub DemoMe( )
    AboutMe    ' Does the same thing.
End Sub
Run Code Online (Sandbox Code Playgroud)

重点是什么?Me 关键字有什么作用吗?对象访问其自身成员的首选方式是什么?

Com*_*ern 8

tldr; 不,尽管在某些情况下它可能有用。


来自 VBA 语言规范 ( 5.3.1.5 ):

作为方法的每个过程都有一个隐式 ByVal 参数,称为当前对象,该对象对应于方法调用的目标对象。当前对象充当具有过程范围的匿名局部变量,其声明的类型是包含方法声明的类模块的类名。在方法激活的持续时间内,当前对象变量的数据值是创建该激活的过程调用的目标对象。当前对象使用方法的 <procedure-body> 中的Me关键字访问 ,但不能分配给或以其他方式修改。

这就是全部,只是一个“自由”局部变量,它指的是正在调用该方法的特定实例。这也恰好是过程调用期间的默认上下文,因此如果代码旨在对当前实例进行操作,则可以省略它。尽管正如@HansPassant 在上面评论中指出的那样,它也允许编辑器绑定到界面并提供 IntelliSense。

也就是说,在某些情况下,您可能想要或必须使用它(这绝不是一个详尽的列表):


命名冲突:

如果您的类具有“隐藏”内置 VBA 函数的成员,则可以使用它来明确作用域:

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
Run Code Online (Sandbox Code Playgroud)

股权检查:

Public Function Equals(other As Object) As Boolean
    If other Is Me Then
        Equals = True
        Exit Function
    End If
    '...
End Function
Run Code Online (Sandbox Code Playgroud)

流畅的功能:

这对于合成对象来说是一个有用的模式——你执行一个动作,然后返回类的实例,这样它们就可以被“链接”起来。Excel 的Range界面在很多情况下都会这样做:

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
Run Code Online (Sandbox Code Playgroud)


Mat*_*don 6

没有什么比this在 Java、C# 或任何其他语言中使用更多的理由:它是一个保留的标识符,代表类的当前实例——你用它做什么取决于你的想象。

对象访问其自身成员的首选方式是什么?

事实上,对象并不需要Me访问它自己的公共接口关键字。与this其他语言一样,我什至认为它是多余的。然而,有时使用 显式限定成员调用可能是一个好主意Me,尤其是当类具有VB_PredeclaredId属性(例如 any UserForm)时:UserForm1在代码隐藏中UserForm1引用产生对类的默认实例的引用,而限定成员调用withMe产生对该类的当前实例的引用。

访问继承成员

VBA用户代码不能做的类继承,但很多VBA类有一个基类。的成员UserForm,当你在代码隐藏的UserForm1,和那些Worksheet当你在代码隐藏的Sheet1,不一定容易找到。但由于继承的成员显示在IntelliSense /auto-complete 中,您可以键入Me.并浏览从基类继承的成员列表,否则您需要了解这些成员才能调用。

一个在自身内部创建自身实例的类?我从未见过的。

你错过了!我一直这样做,以便能够With工厂方法中引用块持有的对象实例- 就像这个 GridCoord 类

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
Run Code Online (Sandbox Code Playgroud)

请注意,虽然GridCoord该类为XY属性公开了一个 getter 和一个 setter,但该IGridCoord接口只公开了 getter。因此,针对IGridCoord接口编写的代码可以有效地使用只读属性。

另一个用途是获取类模块的名称,而无需对其进行硬编码。这在引发自定义错误时特别有用:仅TypeName(Me)用于Source错误。


所述生成器模式众所周知的回报Me,这使得一个“流利API”的设计,使得可以写入代码,递增地建立通过链接的部件的呼叫复杂对象,每个构件返回其中Me(除了最后的Build呼叫时,它返回类的类型被建):

Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
    .WithFoo(42) _
    .WithBar("test") _
    .WithSomething _
    .WithSomethingElse
    .Build
Run Code Online (Sandbox Code Playgroud)