VBA和MS-Access中的Bang符号和点表示法

时间:2010-05-27 18:49:39

标签: ms-access vba vb6 notation

在仔细阅读我documenting的应用程序时,我在访问对象属性/方法等时会遇到一些bang符号的例子。在其他地方,他们使用点符号来表示相同的内容目的

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

3 个答案:

答案 0 :(得分:34)

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

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

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

答案 1 :(得分:27)

bang运算符(!)是访问Collection或其他可枚举对象成员的简写,例如Fields的{​​{1}}属性。

例如,您可以创建ADODB.Recordset并为其添加一些键控项:

Collection

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

  1. Dim coll As Collection Set coll = New Collection coll.Add "First Item", "Item1" coll.Add "Second Item", "Item2" coll.Add "Third Item", "Item3"
    这是最明确的形式。

  2. coll.Item("Item2")
    这是有效的,因为coll("Item2")Item类的默认方法,因此您可以省略它。

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

  4. 人们似乎认为这比它应该更复杂,这就是为什么很难找到一个直截了当的解释。通常,并发症或“不使用爆炸操作员的原因”源于对其实际简单性的误解。当某人对爆炸操作员有问题时,他们倾向于责怪它而不是他们所遇到的问题的真正原因,这通常更为微妙。

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

    在第一种情况下,如果您将代码输入错误为Me!txtPhone并且没有该名称的控件,则代码将无法编译。在第二种情况下,如果您编写Me.txtFone,则不会出现编译错误。相反,如果代码到达使用Me!txtFone的代码行,则代码会出现运行时错误。

    针对bang运算符的论证的问题是这个问题与bang运算符本身无关。它的行为完全符合预期。

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

    由于Me!txtFone实际上是Me!ControlName 1 的简写,因此不应该对您没有得到任何编译时检查以防止错误输入控件名称感到惊讶

    换句话说,如果爆炸操作员是“坏”并且点操作符是“好”,那么你可能会认为

    Me.Controls("ControlName")

    优于

    Me.Controls("ControlName")
    

    因为第一个版本使用点,但在这种情况下,点完全没有任何好处,因为您仍然通过参数访问控件名称。当有另一种编写代码的方式时,它只会“更好”,以便您获得编译时检查。由于VB为每个控件创建属性,因此控件就是这种情况,这就是Me!ControlName 有时推荐Me.ControlName的原因。


    1. 我原先声明Me!ControlName属性是Controls类的默认属性,但David在评论中指出Form不是Controls的默认属性1}}。实际的默认属性返回一个包含 Form内容的集合,这就是为什么bang short-hand仍然有效。

答案 2 :(得分:3)

夫妻俩成为已经发布的两个特殊答案的附录:

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

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

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

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

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

怎么样.Value?
虽然在问题中未明确提及,但上述评论中提到了该主题。 Field对象的默认属性和大多数“数据可绑定”¹控件对象是.Value。由于这是默认属性,因此通常认为总是明确地包含它是不必要的冗长。因此,这是标准做法:

Dim EmployeeLastName As String
EmployeeLastName = Me.tbLastName

而不是:

EmployeeLastName = Me.tbLastName.Value

在键入词典时要注意细微的.Value错误
在某些情况下,此约定可能会导致细微的错误。最值得注意的是 - 而且,如果内存仅供用 - 我实际上遇到的一个是将Field / Control的值用作词典键。

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字典中创建了两个条目。相反,它会在最后一行引发错误,因为我们正在尝试添加重复键。也就是说,tbLastName Control对象本身是键,而不是控件的值。在这种情况下,控制的价值甚至都不重要。

事实上,我希望对象的内存地址(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例程)不会添加新的字典项,因为它是Control对象本身索引字典,而不是Control的值。

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

  • 带有控件类型指示符的前缀表单/报表控件名称(例如tbTextBoxlblLabel等)
  • 使用Me.表示法(例如Me.tbLastName
  • 参阅代码中的表单/报表控件
  • 首先避免使用problematic names创建表/查询字段
  • 发生冲突时使用Me!表示法,例如遗留应用程序(例如Me!Pages
  • 包含隐藏的报表控件以获取对Recordset字段值的访问权限
  • 仅在情况需要增加详细程度(例如,词典键)时明确包含.Value

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