2010-05-27 50 views
18

雖然細讀,我是documenting一個應用程序,我經歷了爆炸符號的一些例子在訪問對象的屬性/方法等,並在其他地方運行它們使用點爲什麼似乎是相同的目的。邦符號和點表示法在VBA和MS-訪問

是否有使用一種或另一種的差異或偏好?一些簡單的Google搜索只會揭示有關此主題的有限信息,有些人會在相反的情況下實際使用它。也許有一個來自MS的編碼標準部分指出了瘋狂的方法?

回答

27

儘管(以前)接受了這個問題的答案,但實際上並不是會員或收集訪問操作員。它做了一件簡單而具體的事情:bang運算符通過將bang運算符之後的文字名稱作爲字符串參數傳遞給該默認成員,從而提供對對象的默認成員的後期綁定訪問。

就是這樣。該對象不一定是一個集合。它不必具有稱爲Item的方法或屬性。所有它需要的是Property GetFunction它可以接受一個字符串作爲第一個參數。

對於更多的細節和證據,請參見我的博客文章討論這個:The Bang! (Exclamation Operator) in VBA

+2

+1不錯的,約書亞。您的博客文章是關於爆炸運營商知識的非常好的昇華。由於MS文檔在這個主題上非常糟糕,我不得不很難找出這個問題。我希望我在15年前閱讀過這一切! – 2013-04-12 00:03:23

+0

我不再在VBA/MS Access中工作,所以我不能真正確認你的發現......你有多確定?你希望這是被接受的答案嗎? – Nitrodist 2013-04-12 15:40:36

+0

@Nitrodist我很確定。我每天都用VBA工作。我的博客文章也包含一些概念驗證代碼。至於它是否應該被接受的答案......只有你可以決定:) – 2013-04-12 16:34:06

24

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將爆炸後的文本作爲參數傳遞給Item方法。

人們似乎會使它比應該更復雜,這就是爲什麼很難找到一個簡單的解釋。通常情況下,複雜性或「不使用砰砰聲操作員的原因」是由於誤解實際上有多簡單。當某人遇到爆炸操作員的問題時,他們往往會責怪它,而不是他們遇到的問題的真正原因,這通常更加微妙。

例如,有些人建議不要使用爆炸操作符來訪問窗體上的控件。因此,Me.txtPhone優於Me!txtPhone。這被認爲是不好的「原因」是Me.txtPhone將在編譯時檢查是否正確,但Me!txtPhone不會。

在第一種情況下,如果你輸錯代碼爲Me.txtFone並沒有與該名稱的控制,你的代碼將無法編譯。在第二種情況下,如果您編寫了Me!txtFone,則不會收到編譯錯誤。相反,如果代碼到達使用Me!txtFone的代碼行,您的代碼將發生運行時錯誤。

與對爆炸運營商的觀點的問題是,這個問題沒有任何與爆炸運營商本身。它的行爲完全符合它的預期。

將控件添加到窗體時,VB會自動向您的窗體添加一個屬性,其名稱與您添加的控件的名稱相同。該屬性是表單類的一部分,因此如果使用dot(「)訪問控件,編譯器可以在編譯時檢查拼寫錯誤。「),運營商(你可以使用點運算符恰恰訪問他們,因爲VB創建了一個名爲控件屬性你)。

由於Me!ControlName實際上是短手Me.Controls("ControlName") ,它不應該被suprising你不要「獲得任何編譯時檢查針對錯誤輸入控件名稱。

換句話說,如果爆炸運算符是‘壞’和點運算符是‘好’,那麼你可能會認爲

Me.Controls("ControlName") 

更好
Me!ControlName 

因爲第一個版本使用一個點,但在這種情況下,因爲您仍然通過參數訪問控制名稱,所以根本沒有更好的點。當有另一種編寫代碼的方式時,只有「更好」才能獲得編譯時檢查。這種情況發生在控件由VB爲您創建每個控件的屬性時發生,這就是爲什麼Me.ControlName有時會比Me!ControlName更值得推薦。


  1. 我原本表示,Controls財產是Form類的默認屬性,但大衛指出,在評論認爲Controls不是Form的默認屬性。實際的默認屬性返回的集合包含的內容Me.Controls,這就是爲什麼爆炸短手仍然有效。
+2

Me.Controls不是*表單對象的默認「屬性」。表單或報表中的Me的默認*集合*是Controls和Fields集合的聯合。作爲獨立的類模塊,沒有默認的集合。否則,以及很好的答案,概括了我不久前發佈的一個答案,我懶得查找! – 2010-05-29 02:36:15

+1

@David:抱歉,我在發佈我的答案時考慮了VB6,其中'Form'類沒有'Fields'集合。我會編輯我的答案來澄清。但是現在你提到它了,技術上'Controls'不是VB6中'Form'的默認特性:有一個名爲'_Default'的隱藏屬性被列爲類的默認成員。不確定它是否是VB6中多個集合的聯合,或者它是否簡單地返回'Controls'。儘管如此。現在你已經讓我想解決這個謎團:-) – 2010-05-29 04:22:38

+0

而coll.Item(「Item2」)並不總是全部的故事。在大多數情況下,您確實是指coll.Item(「Item2」)。值,但是在某些對象引用可接受的地方,對象的默認屬性(.Value)將不會被選中。這可能會導致很難找到錯誤,但這很少見。請注意,這不是一個失敗的!語法本身。 – Bob77 2010-05-29 09:20:42

2

夫婦陷阱作爲增編已經發布兩個特殊的答案:

在形式與報告訪問記錄的字段
Access中的Form對象的默認項是表單的Controls集合和表單記錄集的Fields集合的聯合。如果控件的名稱與字段的名稱衝突,我不確定哪個對象實際返回。由於字段和控件的默認屬性是.Value,它通常是「沒有區別的區別」。換句話說,人們通常不關心這是因爲領域和控制的價值經常是相同的。

小心命名衝突!
Access的窗體和報表設計器默認將綁定控件命名爲與綁定到的記錄集字段相同,這種情況加劇了。我個人採用了以其控制類型前綴命名控件的約定(例如,tbLastName用於綁定到姓氏字段的文本框)。

報告記錄集字段不存在!
我剛纔說的Form對象的默認項是控件和字段的集合。但是,Report對象的默認項目僅是其控件的集合。所以如果有人想使用bang運算符來引用記錄集字段,那麼需要將該字段包含爲(隱藏的,如果需要的話)綁定控件的源。

當心有明確的形式/報表屬性
衝突當一個添加控件到窗體或報表,Access自動創建引用這些控件的屬性。例如,通過參考Me.tbLastName,可從表單的代碼模塊獲得名爲tbLastName的控件。但是,如果Access與現有表單或報表屬性發生衝突,Access將不會創建此類屬性。例如,假設添加一個名爲Pages的控件。在窗體的代碼模塊中參照Me.Pages將返回窗體的Pages屬性,而不是名爲「Pages」的控件。

在此示例中,可以使用Me.Controls("Pages")或隱式使用bang運算符Me!Pages來明確訪問「頁面」控件。但請注意,使用bang運算符意味着Access可能會返回名爲「Pages」的字段(如果該字段存在於表單的記錄集中)。

.Value呢?
雖然在問題中沒有明確提到,但這個話題出現在上述評論中。 Field對象的默認屬性和大多數「數據綁定」¹控制對象爲.Value。由於這是默認屬性,因此通常將其視爲不必要的冗長,以便始終明確包含它。因此,它是標準的做法,這樣做:

Dim EmployeeLastName As String 
EmployeeLastName = Me.tbLastName 

相反的:

EmployeeLastName = Me.tbLastName.Value 

鍵控字典時,請注意微妙的.value的錯誤
有些情況下,這個慣例可能會導致微妙的錯誤。最顯着的 - 如果內存服務,只有 - 我在練習中實際遇到的一個問題是將Field/Control的值用作Dictionary鍵。

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控制對象本身是關鍵,而不是控件的值。在這種情況下,控制的價值並不重要。實際上,我認爲對象的內存地址(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的值。

我個人建議/編碼約定
多年來,我已經採取了以下做法,情況因人而異:

  • 與控制型指標(如tbTextBoxlblLabel前綴窗體/報表控件名稱等)
  • 請參考表格/報表控件使用Me.表示法(例如,Me.tbLastName
  • 避免創建表/查詢字段problematic names首先
  • 使用Me!符號時有衝突,如與傳統應用程序(例如,Me!Pages
  • 包括隱藏報表控件來訪問報告記錄字段值
  • 明確包括.Value只有當局勢需要增加的冗長(例如,字典鍵)

¹什麼是「數據綁定」控制?
基本上,一個控件的ControlSource屬性,如TextBox或ComboBox。一個不可綁定的控件就像一個Label或CommandButton。 TextBox和ComboBox的默認屬性是.Value;標籤和CommandButtons沒有默認屬性。