【问题标题】:Bang Notation and Dot Notation in VBA and MS-AccessVBA 和 MS-Access 中的 Bang Notation 和 Dot Notation
【发布时间】:2011-02-24 19:43:09
【问题描述】:

在阅读我是 documenting 的应用程序时,我在访问对象属性/方法等时遇到了一些 bang 表示法的示例,并且在其他地方,他们使用点表示法来实现看似相同的目的。

使用其中一种是否有区别或偏好?一些简单的谷歌搜索只能揭示有关该主题的有限信息,有些人实际上在相反的情况下使用它。也许某处的 MS 有一个编码标准部分表明了疯狂的方法?

【问题讨论】:

    标签: ms-access vba vb6 notation


    【解决方案1】:

    尽管(以前)已接受此问题的答案,但 bang 实际上不是成员或集合访问运算符。它做了一件简单而具体的事情:bang 运算符提供对对象默认成员的后期绑定访问,方法是将 bang 运算符后面的文字名称作为字符串参数传递给该默认成员。

    就是这样。对象不必是集合。它不必具有名为Item 的方法或属性。它所需要的只是一个Property GetFunction,它们可以接受一个字符串作为第一个参数。

    有关更多详细信息和证明,请参阅我的博客文章讨论此问题:The Bang! (Exclamation Operator) in VBA

    【讨论】:

    • +1 不错,约书亚。您的博文很好地提炼了关于 bang 运算符的知识。我不得不以艰难的方式找出这一点,因为 MS 文档在这个主题上非常糟糕。我希望我在 15 年前就读过这一切!
    • 我不再在 VBA/MS Access 中工作,所以我无法真正确认您的发现...您有多确定?您希望这是接受的答案吗?
    • @Nitrodist 我很确定。我每天都使用 VBA。我的博客文章还包含一些概念验证代码。至于它是否应该是公认的答案......只有你可以决定:)
    • @Nitrodist:我也很确定 Joshua 是对的。我的回答提出了同样的观点,但没有说清楚;-)。例如,请参阅我的答案中的脚注。我回答中的第一句话确实不是措辞的最佳选择——我在陈述一个常见的用例,但看起来我在定义爆炸操作员的目的/行为,这不是故意的。不过,我的回答只是 suggests 它调用了对象的默认成员,而没有真正说出来,所以这个答案肯定比我的回答更重要。
    • 在使用 VB4 时,我一直想知道“扩展如何改变语言规则”。
    【解决方案2】:

    bang 运算符 (!) 是访问 Collection 或其他可枚举对象(例如 ADODB.RecordsetFields 属性)成员的简写。

    例如,您可以创建一个Collection 并向其中添加一些键项:

    Dim coll As Collection
    Set coll = New Collection
    
    coll.Add "First Item", "Item1"
    coll.Add "Second Item", "Item2"
    coll.Add "Third  Item", "Item3"
    

    您可以通过三种方式通过其键访问此集合中的项目:

    1. coll.Item("Item2")
      这是最明确的形式。

    2. coll("Item2")
      这是因为ItemCollection 类的默认方法,所以你可以省略它。

    3. coll!Item2
      这是上述两种形式的简写。在运行时,VB6 获取 bang 之后的文本并将其作为参数传递给 Item 方法。

    人们似乎把它弄得比它应该的更复杂,这就是为什么很难找到一个直截了当的解释。通常,并发症或“不使用 bang 运算符的原因”源于对它实际上有多简单的误解。当某人对 bang 运算符有问题时,他们倾向于归咎于问题,而不是他们遇到问题的真正原因,这通常更微妙。

    例如,有些人建议不要使用 bang 运算符来访问表单上的控件。因此,Me.txtPhone 优于 Me!txtPhone。这被视为不好的“原因”是 Me.txtPhone 将在编译时检查正确性,但 Me!txtPhone 不会。

    在第一种情况下,如果您将代码错误地键入为Me.txtFone,并且没有使用该名称的控件,您的代码将无法编译。在第二种情况下,如果你写了Me!txtFone,你不会得到编译错误。相反,如果您的代码到达使用 Me!txtFone 的代码行,您的代码将因运行时错误而崩溃。

    反对 bang 运算符的论点的问题在于,这个问题与 bang 运算符本身无关。它的行为完全符合它的预期。

    当您向窗体添加控件时,VB 会自动向窗体添加一个与您添加的控件同名的属性。此属性是表单类的一部分,因此如果您使用点 (".") 运算符访问控件,编译器可以在编译时检查拼写错误(并且您可以使用点运算符访问它们,因为 VB 创建了一个命名控件财产)。

    由于Me!ControlName 实际上是Me.Controls("ControlName")1 的简写,因此您没有得到任何针对错误输入控件名称的编译时检查也就不足为奇了。

    换句话说,如果 bang 运算符是“坏”而点运算符是“好”,那么你可能会认为

    Me.Controls("ControlName")
    

    优于

    Me!ControlName
    

    因为第一个版本使用点,但在这种情况下,点并没有更好,因为您仍然通过参数访问控件名称。只有当有另一种方法来编写代码以便您获得编译时检查时,它才会“更好”。由于 VB 会为您创建每个控件的属性,因此控件会出现这种情况,这就是为什么有时建议使用 Me.ControlName 而不是 Me!ControlName


    1. 我最初声明Controls 属性是Form 类的默认属性,但David 在cmets 中指出Controls 不是Form 的默认属性。实际的默认属性返回一个集合,该集合包含Me.Controls 的内容,这就是 bang 简写仍然有效的原因。

    【讨论】:

    • Me.Controls 不是表单对象的默认“属性”。表单或报表中 Me 的默认 collection 是 Controls 和 Fields 集合的联合。在作为独立类模块中,没有默认集合。否则,出色的答案概括了我不久前发布的答案,我懒得查找!
    • @David:很抱歉,当我发布我的答案时,我正在考虑 VB6,其中 Form 类没有 Fields 集合。我将编辑我的答案以澄清。但是现在您提到它,从技术上讲,Controls 也不是 VB6 中Form 的默认属性:有一个名为_Default 的隐藏属性被列为该类的默认成员。不确定它是 VB6 中多个集合的联合还是仅返回 Controls。不过很好抓。现在你让我想解开这个谜团:-)
    • 而且 coll.Item("Item2") 也不总是完整的故事。在大多数情况下,您实际上是指 coll.Item("Item2").Value,但是在某些可以接受对象引用的地方,不会选择对象的默认属性 (.Value)。这可能会导致难以发现错误,但这种情况很少见。请注意,这不是 !然而语法本身。
    • @Bob:我考虑将其添加为另一个示例,有时会错误地指责 bang 运算符。我见过几次有人建议使用rs.Fields("ColumnName").Value,因为rs!ColumnName“并不总是有效”,但正如你所指出的,任何问题都是由于没有明确使用Value属性,在那些它确实有所作为的情况。 rs!ColumnName.Value 本来可以很容易地解决这个问题,所以这只是另一个错误的情况,真正的问题是 VB6 如何以及何时评估默认属性。
    • 我不确定添加 .Value 是否总是有效。从 A2000 开始,表单/报表与基础记录源之间存在脱节,这反映在需要为您在代码中引用的任何字段提供一个带有控制源的控件。也就是如果MyField在recordsource中,但是没有把它作为ControlSource的控件,在代码中引用它是不可靠的,不是因为.要么 !或 .Value,但因为 MS 搞砸了表单/报告与其 Fields 集合之间的关系。
    【解决方案3】:

    作为已发布的两个特殊答案的补充,有几个陷阱:

    访问表单和报告中的记录集字段
    Access 中表单对象的默认项是表单的 Controls 集合和表单记录集的 Fields 集合的并集。如果控件的名称与字段的名称冲突,我不确定实际返回的是哪个对象。由于字段和控件的默认属性都是它们的.Value,因此通常是“没有区别的区别”。换句话说,人们通常不关心它是什么,因为字段和控件的值通常是相同的。

    小心命名冲突!
    Access 的窗体和报表设计器默认将绑定控件命名为与它们绑定的记录集字段相同,从而加剧了这种情况。我个人采用了使用控件类型前缀重命名控件的约定(例如,tbLastName 用于绑定到 LastName 字段的文本框)。

    报告记录集字段不存在!
    我之前说过,Form 对象的默认项是控件和字段的集合。但是,报表对象的默认项只是它的控件集合。因此,如果想使用 bang 运算符引用记录集字段,则需要将该字段作为(隐藏,如果需要)绑定控件的源。

    注意与显式表单/报告属性的冲突
    当向窗体或报表添加控件时,Access 会自动创建引用这些控件的属性。例如,一个名为tbLastName 的控件可以通过引用Me.tbLastName 从表单的代码模块中获得。但是,如果 Access 与现有表单或报表属性冲突,则不会创建此类属性。例如,假设添加了一个名为 Pages 的控件。在表单的代码模块中引用Me.Pages 将返回表单的Pages 属性,而不是名为“Pages”的控件。

    在本例中,可以使用Me.Controls("Pages") 显式访问“Pages”控件,也可以使用 bang 运算符Me!Pages 隐式访问“Pages”控件。但请注意,使用 bang 运算符意味着如果表单的记录集中存在一个名为“Pages”的字段,Access 可能会改为返回一个名为“Pages”的字段。

    .Value 呢?
    尽管问题中没有明确提及,但该主题出现在上述 cmets 中。 Field 对象和大多数“数据绑定”¹控件对象的默认 属性.Value。由于这是默认属性,当返回对象本身没有意义时,VBA 将隐式返回 .Value 属性的值。因此,这样做是常见的做法......

    Dim EmployeeLastName As String
    EmployeeLastName = Me.tbLastName
    

    ...而不是这个...

    EmployeeLastName = Me.tbLastName.Value
    

    以上两个语句产生相同的结果,因为EmployeeLastName 是一个字符串。

    键入字典时要小心细微的 .Value 错误
    在某些情况下,这种约定可能会导致细微的错误。最值得注意的——如果没记错的话,只有——我在实践中实际遇到的一个是使用字段/控件的值作为字典键时。

    Set EmployeePhoneNums = CreateObject("Scripting.Dictionary")
    Me.tbLastName.Value = "Jones"
    EmployeePhoneNums.Add Key:=Me.tbLastName, Item:="555-1234"
    Me.tbLastName.Value = "Smith"
    EmployeePhoneNums.Add Key:=Me.tbLastName, Item:="555-6789"
    

    人们可能会认为上面的代码会在EmployeePhoneNums 字典中创建两个条目。相反,它会在最后一行引发错误,因为我们正在尝试添加重复键。也就是说,tbLastNameControl 对象本身是键,而不是控件的值。在这种情况下,控件的值甚​​至无关紧要。

    事实上,我预计对象的内存地址 (ObjPtr(Me.tbLastName)) 可能是在幕后用于索引字典的内容。我做了一个快速测试,似乎证明了这一点。

    'Standard module:
    Public testDict As New Scripting.Dictionary
    Sub QuickTest()
        Dim key As Variant
        For Each key In testDict.Keys
            Debug.Print ObjPtr(key), testDict.Item(key)
        Next key
    End Sub
    
    'Form module:
    Private Sub Form_Current()
        testDict(Me.tbLastName) = Me.tbLastName.Value
        Debug.Print ObjPtr(Me.tbLastName); "..."; Me.tbLastName
    End Sub
    

    运行上述代码时,每次关闭并重新打开表单时,都会添加一个字典项。从一个记录移动到另一个记录(并因此导致对 Form_Current 例程的多次调用)不会添加新的字典项,因为它是控件对象本身索引字典,而不是控件的值。

    我的个人建议/编码约定
    多年来,我采用了以下做法,YMMV:

    • 为表单/报表控件名称加上控件类型指示符前缀(例如,tbTextBoxlblLabel 等)
    • 请参阅代码中使用Me. 表示法(例如Me.tbLastName)的表单/报表控件
    • 首先避免使用problematic names 创建表/查询字段
    • 在存在冲突时使用 Me! 表示法,例如与旧版应用程序(例如,Me!Pages
    • 包含隐藏的报表控件以访问报表记录集字段值
    • 仅当情况需要增加详细信息(例如,字典键)时才显式包含 .Value

    ¹ 什么是“数据可绑定”控件?
    基本上,具有ControlSource 属性的控件,例如 TextBox 或 ComboBox。不可绑定控件类似于标签或命令按钮。 TextBox 和 ComboBox 的默认属性是.Value;标签和命令按钮没有默认属性。

    【讨论】:

    • 我希望我在几年前大量使用 Access 时就知道所有这些要点……不得不以艰难的方式发现它们。有趣的是它的official name 似乎是字典查找运算符,只是上面的示例错误案例:)
    猜你喜欢
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    • 2021-09-04
    • 2021-12-27
    • 2021-08-14
    • 2017-11-15
    • 2021-12-09
    相关资源
    最近更新 更多