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

6

当这两个子程序在类内时,它们执行相同的操作。

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

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

这有什么意义呢?Me 关键字有什么作用吗?一个对象访问自己的成员的首选方式是什么?


我指的是它所在的对象,因此 AboutMe 位于类中。如果您有另一个实例,比如说这是 Class1,您需要声明 dim c as Class1,一旦您在 Class1 中创建了一个实例,您需要告诉编译器您正在使用哪个类,即持有类或创建的实例,在那里,me.class1.aboutme 在逻辑上是有效的。您还可以为工作簿中的每个单元格创建一个类,然后您可以从 B1 的类引用 A1 的类。此外,如果有一个名为 AboutMe 的公共函数/子程序,这也会有所帮助。 :o) - Nathan_Sav
一个类在自身内部创建一个实例?我从未见过。 - PBeezy
2
一个类创建自己的实例是非常正常的 - 想象一下 Class1.Copy As Class1 - Comintern
当然,考虑使用类来表示人类,这样你就可以有一个父子关系,其中clsHuman可以作为父类,包含一个clsHuman数组来表示它的子类。 - Nathan_Sav
3
自动完成代码非常有用。当您键入“Me.”时,您会得到该类的成员列表。少打字,减少错误,鼓励使用自我记录的过程名称。例如,“ShowAboutMeDialog”,使用起来不需要更多的努力。 - Hans Passant
3个回答

8
不需要,尽管在某些情况下它可能会有用。

来自VBA语言规范(5.3.1.5):

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

就是这样,只是一个"免费"的本地变量,用于引用调用该方法的特定实例。在它们调用期间,这也恰好是过程的默认上下文,因此如果代码旨在操作当前实例,则可以省略它。尽管如同@HansPassant在上面的评论中指出的那样,它还允许编辑器绑定到接口并提供智能感知。

虽然如此,但有几种情况下您需要使用它(这绝不是穷尽性列表):


命名冲突:

如果您的类具有隐藏内置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

权益检查:

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

流畅的函数:

这是一个有用的对象组合模式 - 您执行操作,然后返回类的实例,以便它们可以“链接”。在许多情况下,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

6
与在Java、C#或其他语言中使用this一样,使用Me也没有特别的原因:它是一个保留标识符,表示类的当前实例 - 如何使用这个实例取决于你的想象力。
“一个对象访问自己成员的首选方式是什么?”
事实上,对象不需要使用Me关键字来访问其公共接口。与其他语言中的this一样,我甚至会称其为多余。然而,有时候明确地限定成员调用使用Me可以是一个好主意,特别是当类具有VB_PredeclaredId属性(例如任何):在代码中引用UserForm1,在UserForm1中代码后台将产生对该类的默认实例的引用,而使用Me限定成员调用将产生对该类的当前实例的引用。
访问继承成员
VBA用户代码不能进行类继承,但是很多VBA类确实有一个基类。当您在UserForm1的代码后台中时,UserForm的成员以及当您在Sheet1的代码后台中时,Worksheet的成员并不容易找到。但由于继承的成员出现在智能感知/自动完成中,您可以输入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

请注意,虽然GridCoord类公开了XY属性的getter和setter,但IGridCoord接口只公开了getter。因此,针对IGridCoord接口编写的代码实际上正在使用只读属性。
另一个用途是获取类模块的名称,而无需硬编码。这在引发自定义错误时特别有用:只需使用TypeName(Me)作为错误的Source
Builder Pattern以返回Me而著称,这使得可以通过链接成员调用逐步构建复杂对象的“流畅API”设计成为可能,其中每个成员都返回Me(除了最后的Build调用,它返回正在构建的类的类型)。
Dim thing As Something
Set builder = New ThingBuilder
Set thing = builder _
    .WithFoo(42) _
    .WithBar("test") _
    .WithSomething _
    .WithSomethingElse
    .Build

1

@PBeezy: 除了我的评论:

Me指的是它来自的对象,因此AboutMe驻留在类中。如果您有另一个实例,比如说这是Class1,您需要dim c as Class1,一旦您在Class1中创建了一个实例,您需要告诉编译器您正在使用哪个类,即持有类或在哪里创建的实例,在这种情况下,me.class1.aboutme在逻辑上是有效的。您还可以为工作簿中的每个单元格创建一个类,然后可以从B1的类引用A1的类。此外,如果有一个名为AboutMe的公共函数/子程序,这也很有帮助。

类(clsPerson)

Public c1 As clsPerson
Public strPersonName As String

Public Function NAME_THIS_PERSON(strName As String)
    strPersonName = strName
End Function

Public Function ADD_NEW_CHILD(strChildName As String)
    Set c1 = New clsPerson
    c1.strPersonName = strChildName
End Function

普通模块

Sub test()

Dim c As New clsPerson

c.NAME_THIS_PERSON "Mother"
c.ADD_NEW_CHILD "Nathan"

Debug.Print c.strPersonName
Debug.Print c.c1.strPersonName

End Sub

给出这些结果。
Mother
Nathan

读取第一行! - Nathan_Sav
如果你很在意的话,可以把它标记下来,但我并不在意。 :o) - Nathan_Sav
太好了,希望你现在对此感觉更好 :o) - Nathan_Sav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接