Jos*_*nig 35
尽管(以前)接受了这个问题的答案,但实际上并不是成员或集合访问运营商.它做了一个简单而具体的事情:bang运算符通过将bang运算符后面的文字名称作为字符串参数传递给该默认成员,提供对对象的默认成员的后期绑定访问.
而已.该对象不必是一个集合.它不必具有调用的方法或属性Item.它需要的只是一个Property Get或者Function可以接受一个字符串作为第一个参数.
有关更多细节和证据,请参阅我的博客文章讨论:Bang!(VBA中的感叹号操作员)
Mik*_*oss 27
bang operator(!)是访问一个Collection或其他可枚举对象的成员的简写,例如a的Fields属性ADODB.Recordset.
例如,您可以创建一个Collection并向其添加一些键控项:
Dim coll As Collection
Set coll = New Collection
coll.Add "First Item", "Item1"
coll.Add "Second Item", "Item2"
coll.Add "Third Item", "Item3"
Run Code Online (Sandbox Code Playgroud)
您可以通过三种方式通过其键访问此集合中的项目:
coll.Item("Item2")
这是最明确的形式.
coll("Item2")
这是因为Item是类的默认方法Collection,所以你可以省略它.
coll!Item2
这是上述两种形式的简写.在运行时,VB6在爆炸后获取文本并将其作为参数传递给Item方法.
人们似乎比这更复杂,这就是为什么很难找到一个直截了当的解释.通常,并发症或"不使用爆炸操作员的原因"源于对其实际简单性的误解.当某人对爆炸操作员有问题时,他们倾向于责怪它而不是他们所遇到的问题的真正原因,这通常更为微妙.
例如,有些人建议不要使用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,它不应该被suprising你没有得到任何编译时检查针对错误输入控件名称.
换句话说,如果爆炸操作员是"坏"而点操作员是"好",那么你可能会想
Me.Controls("ControlName")
Run Code Online (Sandbox Code Playgroud)
比...更好
Me!ControlName
Run Code Online (Sandbox Code Playgroud)
因为第一个版本使用点,但在这种情况下,点完全没有任何好处,因为您仍然通过参数访问控件名称.当有另一种编写代码的方式时,它只会"更好",以便您获得编译时检查.由于VB为您创建了每个控件的属性,因此控件就是这种情况,这就是为什么Me.ControlName有时会推荐它Me!ControlName.
Controls属性是Form该类的默认属性,但David在注释中指出这Controls不是默认属性Form.实际的默认属性返回一个包含内容的集合Me.Controls,这就是bang short-hand仍然有效的原因.几个问题可以作为已经发布的两个特殊答案的附录:
访问窗体与报表中
的记录集字段Access 中窗体对象的默认项是窗体的 Controls 集合和窗体记录集的 Fields 集合的联合。如果控件名称与字段名称冲突,我不确定实际返回的是哪个对象。由于字段和控件的默认属性都是它们的.Value,因此通常是“没有区别的区别”。换句话说,人们通常不关心它是哪个,因为字段和控件的值通常是相同的。
小心命名冲突!
Access 的窗体和报表设计器默认将绑定控件命名为与其绑定的记录集字段相同的名称,从而加剧了这种情况。我个人采用了重命名控件及其控件类型前缀的约定(例如,tbLastName对于绑定到LastName字段的文本框)。
报告记录集字段不存在!
我之前说过 Form 对象的默认项是 Controls 和 Fields 的集合。但是,Report 对象的默认项只是它的控件集合。因此,如果要使用 bang 运算符引用记录集字段,则需要将该字段作为(隐藏的,如果需要)绑定控件的源包括在内。
注意与显式窗体/报表属性的冲突
当向窗体或报表添加控件时,Access 会自动创建引用这些控件的属性。例如,tbLastName通过引用Me.tbLastName. 但是,如果与现有窗体或报表属性冲突,Access 将不会创建此类属性。例如,假设添加了一个名为 Pages 的控件。Me.Pages在表单的代码模块中引用将返回表单的Pages属性,而不是名为“Pages”的控件。
在此示例中,可以Me.Controls("Pages")使用 bang 运算符显式或隐式地访问“Pages”控件Me!Pages。但是请注意,使用 bang 运算符意味着如果表单的记录集中存在名为“Pages”的字段,则 Access 可能会返回该字段。
.Value 呢?
尽管在问题中没有明确提及,但在上述评论中提到了这个话题。Field 对象和大多数“数据可绑定”¹ Control 对象的默认属性是.Value. 由于这是默认属性,.Value当返回对象本身没有意义时,VBA 将隐式返回该属性的值。因此,通常的做法是这样做......
Dim EmployeeLastName As String
EmployeeLastName = Me.tbLastName
Run Code Online (Sandbox Code Playgroud)
......而不是这个......
EmployeeLastName = Me.tbLastName.Value
Run Code Online (Sandbox Code Playgroud)
以上两个语句产生相同的结果,因为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"
Run Code Online (Sandbox Code Playgroud)
人们可能会期望上面的代码在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
Run Code Online (Sandbox Code Playgroud)
运行上述代码时,每次关闭并重新打开表单时,都会添加一个字典项。从记录移动到记录(从而导致多次调用 Form_Current 例程)不会添加新的字典项,因为它是 Control 对象本身索引字典,而不是 Control 的值。
我的个人建议/编码约定
多年来,我采用了以下实践,YMMV:
tbTextBox,lblLabel等)Me.符号(例如,Me.tbLastName)在代码中引用表单/报告控件Me!当存在冲突时使用符号,例如与遗留应用程序(例如,Me!Pages).Value仅当情况需要增加冗长时才显式包含(例如,字典键)¹什么是“数据可绑定”控件?
基本上,具有ControlSource属性的控件,例如 TextBox 或 ComboBox。不可绑定控件类似于 Label 或 CommandButton。TextBox 和 ComboBox 的默认属性都是.Value; 标签和命令按钮没有默认属性。