嗯,你每天都会学到新东西!
我本来打算解释一下,如果你说引用是[Forms]![FETT List]![FETT Search],那么解释起来就容易了,作为对[FETT Search]控件的引用在 [FETT 列表] 表格上。但如果没有父集合(表单报告),它在 SQL 语句中的任何上下文中看起来都不是有效的引用。
但后来我想测试一下,发现(令我惊讶的是)这个 SQL 语句在 Access 表单中被视为有效:
SELECT [tblCustomer]![LastName] AS LastName
FROM tblCustomer;
在 Access 中,这 100% 等同于这条 SQL 语句:
SELECT tblCustomer.LastName
FROM tblCustomer;
……所以我不明白为什么有人会写它,除非他们忘记了上下文(或者一开始就没有理解它)。这可能是别名出错的情况,但这不是我认为好的形式。
现在,关于!(砰)与.(点)的一般性问题的长答案:
通常,在 Access 中,bang 运算符描述了对象及其项的默认集合。点运算符描述了一个对象及其方法、属性和成员。
这适用于Access,适用于Access对象和Access的对象模型。
但您也在 Access 中使用 SQL,因此您在 SQL 中也有TableName.FieldName,其中点运算符分隔默认集合中的项目。 TableName.FieldName 可以被认为是TableName.Fields("FieldName") 的缩写,正如您发现Forms!MyForm!MyControl 等同于Forms!MyForm.Controls("MyControl")。但这条规则不适用于 SQL —— TableName.Fields("FieldName") 不是有效的 SQL,只有 TableName.FieldName 是。
因此,您必须明确控制您正在使用的命名空间的范式,即,它是 Access 命名空间还是 SQL 命名空间。
Forms!MyForm 也等价于Forms.Item("MyForm"),因此超长表单为Forms.Items("MyForm").Controls("MyControl")。请注意 bang 运算符是如何使用点运算符的较长形式版本的快捷方式,因此 bang 运算符比点运算符更常用。另请注意,当您需要引用名称存储在变量中的项目时,最终会使用较长的形式,而这对于 bang 运算符是不可能的:
Dim strForm As String
strForm = "MyForm"
' This is OK
Debug.Print Forms(strForm).Controls.Count
' This is not
Debug.Print Forms!strForm.Controls.Count
此外,在 VBA 代码中,Microsoft 设计了一些东西来混淆表单和报告中的这种区别,过去 Me!MyFavoriteControl 作为控件引用是合法的,而 Me.MyFavoriteControl 仅作为引用是合法的到自定义属性(或模块级变量,它将是对象的成员)。您还可能不明智地命名一个函数或子“MyFavoriteControl”,并且可以使用点运算符来引用它。
但是随着 VBA 的引入,MS 在所有控件周围引入了隐式创建(和维护)的隐藏属性包装器,以便您可以使用点运算符。这有一个巨大的优势,那就是控制引用的编译时检查。也就是说,如果您键入 Me.MyFavoriteControl 并且在表单/报告的命名空间中没有该名称的控件并且没有任何其他类型的具有该名称的成员,那么您将收到编译时错误(实际上,您会离开出错的代码行后立即通知错误)。所以,如果你有这个代码:
Debug.Print Me.Control1
...并且您将 Control1 重命名为 MyControl,下次编译代码时会出现错误。
编译时检查的缺点是什么?好吧,有几件事:
-
程序员一眼就能看懂代码。在过去,Me!Reference 是指表单/报告的默认集合中的一个项目(它是 Fields 和 Controls 集合的联合)。但是 Me.Reference 可以是控件或字段或自定义属性或公共模块级变量或公共子/函数或,或,或...因此,它牺牲了直接的代码可理解性。
-
您依赖于 VBA 的隐式行为及其编译。虽然这通常是一件好事(特别是如果您照顾好您的代码),VBA compilation is very complex and subject to corruption。多年来,有经验的开发人员报告说,使用点运算符会使代码更容易损坏,因为它添加了另一层隐藏代码,可能与您可以的应用程序部分不同步> 明确改变。
-
由于您无法控制那些隐式属性包装器,因此当它们出错时,您必须从头开始重新创建您的模块承载对象(通常 SaveAsText 足以清除损坏而不会丢失任何内容)。
因此,许多有经验的开发人员(包括我自己)不使用点运算符来控制表单/报告。
如果您使用一套标准的命名约定,这并没有像某些人认为的那样大。例如,对于表单上的绑定控件,让它们使用默认名称(即,控件绑定到的字段的名称)。如果我不在代码中引用控件,我永远不会更改它的名称。但是我第一次在代码中引用它时,我更改了它的名称,以便控件名称与它所绑定的字段的名称不同(这种消除歧义在某些上下文中至关重要)。因此,当我决定在代码中引用它时,名为 MyField 的文本框变成了 txtMyField。编写代码后,我唯一一次更改字段名称是如果我以某种方式确定该字段名称错误。在这种情况下,查找/替换很容易。
有些人争辩说他们不能放弃 Intellisense,但是当你使用 bang 运算符时,你完全放弃它是不正确的。是的,您放弃了“真正智能”的 Intellisense,即将 Intellisense 列表限制为所选对象的方法/属性/成员的版本,但我不需要它——我需要 Intellisense 来保存击键,并且使用 Ctrl+SPACEBAR 可以获得完整的 Intellisense 列表,该列表会像特定于上下文的 Intellisense 一样自动完成,然后可以使输入短路。
点/bang 混淆的另一个领域是 VBA 代码中的 DAO 记录集,其中您使用点运算符表示用于打开记录集的 SQL,而 bang 运算符用于引用结果记录集中的字段:
Dim rs As DAO.Recordset
Set rs = CurrentDB.OpenRecordset("SELECT MyTable.MyField FROM MyTable;")
rs.MoveFirst
Debug.Print rs!MyField
rs.Close
Set rs = Nothing
如果您牢记您在哪个命名空间中工作,那就不会那么混乱了——点用于 SQL 语句,而 bang 用于 DAO 代码。
所以,总结一下:
-
在 SQL 中,您对表中的字段使用点运算符。
-
在表单和报表中,您可以将 bang 运算符用于控件,将点运算符用于属性/方法(虽然您也可以使用点运算符,但不一定可取)。
-
在 VBA 代码中,对表单和报表上的控件的引用可以使用点或 bang,尽管点可能容易导致代码损坏。
-
在 SQL 中,您可能会看到使用了 bang 运算符,但前提是在 Access 表单或报表上引用了“Form!FormName!ControlName”或“Report!ReportName!ControlName”形式的控件。
-
在使用 DAO 记录集的 VBA 代码中,您可能会看到点号和 bang 运算符,前者用于定义用于打开记录集的 SQL,后者用于在打开记录集后引用结果记录集中的字段.
这对你来说够复杂吗?