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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2923957/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 10:29:08  来源:igfitidea点击:

Bang Notation and Dot Notation in VBA and MS-Access

ms-accessvbavb6notation

提问by Nitrodist

While perusing an application that I'm documenting, I've run across some examples of bang notation in accessing object properties/methods, etc. and in other places they use dot notation for what seems like the same purpose.

在细读我正在记录的应用程序时,我遇到了一些在访问对象属性/方法等时使用 bang 符号的示例,在其他地方,它们使用点符号来实现似乎相同的目的。

Is there a difference or preference to using one or the other? Some simple googling only reveals limited information on the subject with some people actually using it in opposite cases. Perhaps there is a coding standards section from MS somewhere that indicates the method of madness?

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

回答by Joshua Honig

Despite the (formerly) accepted answer to this question, the bang is not in fact a member or collection access operator. It does one simple and specific thing: The bang operator provides late-bound access to the default member of an object, by passing the literal name following the bang operator as a string argument to that default member.

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

That's it. The object doesn't have to be a collection. It doesn't have to have a method or property called Item. All it needs is a Property Getor Functionwhich can accept a string as the first argument.

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

For much more detail and proof, see my blog post discussing this: The Bang! (Exclamation Operator) in VBA

有关更多详细信息和证据,请参阅我的博客文章:The Bang!VBA中的(感叹号)

回答by Mike Spross

The bang operator (!) is shorthand for accessing members of a Collectionor other enumerable object, such as the Fieldsproperty of an ADODB.Recordset.

bang 运算符 ( !) 是访问 aCollection或其他可枚举对象的成员的简写,例如 a 的Fields属性ADODB.Recordset

For example, you can create a Collectionand add a few keyed items to it:

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

Dim coll As Collection
Set coll = New Collection

coll.Add "First Item", "Item1"
coll.Add "Second Item", "Item2"
coll.Add "Third  Item", "Item3"

You can access an item in this collection by its key in three ways:

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

  1. coll.Item("Item2")
    This is the most explicit form.

  2. coll("Item2")
    This works because Itemis the default method of the Collectionclass, so you can omit it.

  3. coll!Item2
    This is short-hand for both of the above forms. At run-time, VB6 takes the text after the bang and passes it as a parameter to the Itemmethod.

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

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

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

People seem to make this more complicated than it should be, which is why it's hard to find a straightforward explanation. Usually the complications or "reasons not to use the bang operator" stem from a misunderstanding of how simple it actually is. When someone has a problem with the bang operator, they tend to blame it instead of the real cause of the problem they are having, which is often more subtle.

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

For example, some people recommend not using the bang operator to access controls on a form. Thus, Me.txtPhoneis preferred over Me!txtPhone. The "reason" this is seen as bad is that Me.txtPhonewill be checked at compile-time for correctness, but Me!txtPhonewon't.

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

In the first case, if you mistype the code as Me.txtFoneand there is no control with that name, your code won't compile. In the second case, if you wrote Me!txtFone, you won't get a compile error. Instead, your code will blow up with a run-time error if it reaches the line of code that used Me!txtFone.

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

The problem with the argument against the bang operator is that this problem has nothing to do with the bang operator itself. It's behaving exactly the way it's supposed to.

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

When you add a control to a form, VB automatically adds a property to your form with the same name as the control you added. This property is part of the form's class, so the compiler can check for typos at compile-time if you access controls using the dot (".") operator (and you can access them using the dot operator precisely because VB created a named control property for you).

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

Since Me!ControlNameis actually short-hand for Me.Controls("ControlName")1, it should not be suprising that you don't get any compile-time checks against mistyping the control name.

由于Me!ControlName实际上是短手Me.Controls("ControlName")1,它不应该被suprising你没有得到任何编译时检查针对错误输入控件名称。

Put another way, if the bang operator is "bad" and the dot operator is "good", then you might think

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

Me.Controls("ControlName")

is better than

Me!ControlName

because the first version uses a dot, but in this case, the dot isn't any better at all, since you are still accessing the control name via a parameter. It's only "better" when there is an alternative way to write the code such that you will get compile-time checking. This happens to be the case with controls due to VB creating properties for each control for you, and this is why Me.ControlNameis sometimes recommended over Me!ControlName.

因为第一个版本使用了一个点,但在这种情况下,点根本没有任何好处,因为您仍然通过参数访问控件名称。只有当有一种替代方法来编写代码以便您进行编译时检查时,它才会“更好”。由于 VB 为您为每个控件创建属性,控件恰好就是这种情况,这就是为什么Me.ControlName有时建议使用Me!ControlName.



  1. I had originally stated that the Controlsproperty was the default property of the Formclass, but David pointed out in the comments that Controlsisn't the default property of Form. The actual default property returns a collection that includesthe contents of Me.Controls, which is why the bang short-hand still works.
  1. 我最初声明该Controls属性是Form该类的默认属性,但 David 在评论中指出这Controls不是Form. 实际的默认属性返回一个包含内容的集合Me.Controls,这就是为什么 bang 简写仍然有效的原因。

回答by mwolfe02

Couple gotchas to serve as addenda to the two exceptional answers already posted:

几个问题可以作为已经发布的两个特殊答案的附录:

Accessing recordset fields in forms vs. reports
The default item of Form objects in Access is a union of the form's Controls collection and the form recordset's Fields collection. If the name of a control conflicts with the name of a field, I'm not sure which object is actually returned. Since the default property of both a field and a control is their .Value, it's often a "distinction without a difference." In other words, one normally doesn't care which it is because the values of the field and control are often the same.

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

Beware of naming conflicts!
This situation is exacerbated by Access's Form and Report designer defaulting to naming bound controls the same as the recordset field to which they are bound. I've personally adopted the convention of renaming controls with their control type prefix (e.g., tbLastNamefor the text box bound to the LastNamefield).

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

Report recordset fields aren't there!
I said earlier the Form object's default item is a collection of Controls and Fields. However, the Report object's default item is only its collection of Controls. So if one wants to refer to a recordset field using the bang operator, one needs to include that field as the source for a (hidden, if desired) bound control.

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

Beware conflicts with explicit form/report properties
When one adds controls to a form or report, Access automatically creates properties that refer to these controls. For example, a control named tbLastNamewould be available from a form's code module by referring to Me.tbLastName. However, Access will not create such a property if it conflicts with an existing form or report property. For example, assume one adds a control named Pages. Referring to Me.Pagesin the form's code module will return the form's Pagesproperty, not the control named "Pages".

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

In this example, one could access the "Pages" control explicitly using Me.Controls("Pages")or implicitly using the bang operator, Me!Pages. Be aware, though, that using the bang operator means that Access might instead return a field named "Pages" if one exists in the form's recordset.

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

What about .Value?
Though not explicitly mentioned in the question, this topic came up in the above comments. The default propertyfor Field objects and most "data-bindable"1 Control objects is .Value. Since this is the default property, it's generally considered unnecessarily verbose to always include it explicitly. Thus, it's standard practice to do this:

.Value 呢?
尽管在问题中没有明确提及,但在上述评论中提到了这个话题。Field 对象和大多数“数据可绑定”1 Control 对象的默认属性.Value. 由于这是默认属性,通常认为总是显式包含它是不必要的冗长。因此,执行此操作是标准做法:

Dim EmployeeLastName As String
EmployeeLastName = Me.tbLastName

Instead of:

代替:

EmployeeLastName = Me.tbLastName.Value

Beware the subtle .Value bug when keying dictionaries
There are some cases where this convention can cause subtle bugs. The most notable--and, if memory serves, only--one I've actually run into in practice is when using the value of a Field/Control as a Dictionary key.

键入字典时要注意微妙的 .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"

One would likely expect that the above code creates two entries in the EmployeePhoneNumsdictionary. Instead, it throws an error on the last line because we are trying to add a duplicate key. That is, the tbLastNameControl object itself is the key, not the value of the control. In this context, the control's value does not even matter.

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

In fact, I expect that the object's memory address (ObjPtr(Me.tbLastName)) is likely what's being used behind the scenes to index the dictionary. I did a quick test that seems to bear this out.

事实上,我希望对象的内存地址 ( 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

When running the above code, exactly one dictionary item is added each time the form is closed and re-opened. Moving from record to record (and thus causing multiple calls to the Form_Current routine) does not add new dictionary items, because it is the Control object itself indexing the dictionary, and not the Control's value.

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

My personal recommendations/coding conventions
Over the years, I've adopted the following practices, YMMV:

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

  • Prefix Form/Report control names with control type indicators (e.g., tbTextBox, lblLabel, etc.)
  • Refer to Form/Report controls in code using Me.notation (e.g., Me.tbLastName)
  • Avoid creating table/query fields with problematic namesin the first place
  • Use Me!notation when there are conflicts, such as with legacy applications (e.g., Me!Pages)
  • Include hidden report controls to gain access to report Recordset field values
  • Explicitly include .Valueonly when the situation warrants the added verbosity (e.g., Dictionary keys)
  • 与控制型指标前缀窗体/报表控件名称(如tbTextBoxlblLabel等)
  • 使用Me.符号(例如,Me.tbLastName)在代码中引用表单/报告控件
  • 避免创建与表/查询字段有问题的名字摆在首位
  • Me!当存在冲突时使用符号,例如与遗留应用程序(例如,Me!Pages
  • 包括隐藏的报表控件以访问报表 Recordset 字段值
  • .Value仅当情况需要增加冗长时才显式包含(例如,字典键)


1 What's a "data-bindable" control?
Basically, a control with a ControlSourceproperty, such as a TextBox or ComboBox. A non-bindable control would be something like a Label or CommandButton. The default property of both a TextBox and ComboBox is .Value; Labels and CommandButtons have no default property.

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