Нотация взрыва и нотация точек в VBA и MS-Access - PullRequest
25 голосов
/ 27 мая 2010

Просматривая приложение, которое я документирую , я наткнулся на несколько примеров записи взрыва при доступе к свойствам / методам объекта и т. Д., А в других местах они используют точечную запись для того, что кажется той же цели.

Есть ли разница или предпочтение в использовании того или другого? Некоторое простое прибегание к помощи показывает только ограниченную информацию о предмете с некоторыми людьми, фактически использующими это в противоположных случаях. Возможно, где-то есть раздел стандартов кодирования от MS, который указывает метод безумия?

Ответы [ 3 ]

34 голосов
/ 12 апреля 2013

Несмотря на (ранее) принятый ответ на этот вопрос, взрыв фактически не является участником или оператором доступа к коллекции. Он делает одну простую и конкретную вещь: Оператор bang обеспечивает доступ с поздним связыванием к элементу объекта по умолчанию, передавая литеральное имя, следующее за оператором bang, в качестве строкового аргумента этому элементу по умолчанию. * Вот и все. Объект не должен быть коллекцией. Он не должен иметь метод или свойство с именем Item. Все, что ему нужно - это Property Get или Function, который может принять строку в качестве первого аргумента.

Для получения более подробной информации и доказательств см. Мой пост в блоге, обсуждающий это: The Bang! (Восклицательный оператор) в VBA

27 голосов
/ 28 мая 2010

Оператор взрыва (!) является сокращением для доступа к членам Collection или другого перечислимого объекта, такого как свойство 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"

Получить доступ к элементу в этой коллекции можно по его ключу тремя способами:

  1. coll.Item("Item2")
    Это наиболее явная форма.

  2. coll("Item2")
    Это работает, потому что Item является методом по умолчанию для класса Collection, поэтому вы можете его опустить.

  3. coll!Item2
    Это сокращение для обеих вышеуказанных форм. Во время выполнения VB6 берет текст после взрыва и передает его в качестве параметра методу Item.

Люди, кажется, делают это более сложным, чем должно быть, поэтому трудно найти прямое объяснение. Обычно осложнения или «причины не использовать оператор взрыва» возникают из-за неправильного понимания того, насколько все просто. Когда у кого-то возникает проблема с оператором взрыва, он склонен винить его вместо реальной причины проблемы, которая у него есть, которая часто бывает более тонкой.

Например, некоторые люди рекомендуют не использовать оператор взрыва для доступа к элементам управления в форме. Таким образом, Me.txtPhone предпочтительнее, чем Me!txtPhone. Причина, по которой это считается плохой, заключается в том, что Me.txtPhone будет проверен во время компиляции на правильность, а Me!txtPhone - нет.

В первом случае, если вы неправильно наберете код как Me.txtFone и с этим именем нет элемента управления, ваш код не будет скомпилирован. Во втором случае, если вы написали Me!txtFone, вы не получите ошибку компиляции. Вместо этого ваш код взорвется с ошибкой во время выполнения, если он достигнет строки кода, которая использовала Me!txtFone.

Проблема с аргументом против оператора bang заключается в том, что эта проблема не имеет ничего общего с самим оператором bang. Он ведет себя точно так, как и должен.

Когда вы добавляете элемент управления в форму, VB автоматически добавляет свойство в форму с тем же именем, что и добавленный вами элемент управления. Это свойство является частью класса формы, поэтому компилятор может проверять опечатки во время компиляции, если вы обращаетесь к элементам управления с помощью оператора точки (".") (И вы можете получить к ним доступ с помощью оператора точки именно потому, что VB создал именованный элемент управления собственность для вас).

Поскольку Me!ControlName на самом деле является сокращением для Me.Controls("ControlName") 1 , не должно вызывать удивления то, что вы не получите никаких проверок во время компиляции на предмет неправильного ввода имени элемента управления.

Другими словами, если оператор взрыва "плохой", а оператор точки - "хороший", вы можете подумать

Me.Controls("ControlName")

лучше чем

Me!ControlName

потому что в первой версии используется точка, но в этом случае точка ничуть не лучше, так как вы все еще получаете доступ к имени элемента управления через параметр. Это только «лучше», когда есть альтернативный способ написания кода, так что вы получите проверку во время компиляции. Это происходит в случае с элементами управления из-за того, что VB создает свойства для каждого элемента управления для вас, и именно поэтому Me.ControlName иногда рекомендуется вместо Me!ControlName.


  1. Первоначально я заявлял, что свойство Controls является свойством по умолчанию класса Form, но Дэвид указал в комментариях, что Controls не является свойством по умолчанию Form. Фактическое свойство по умолчанию возвращает коллекцию, которая включает содержимое Me.Controls, поэтому сокращенная комбинация bang по-прежнему работает.
3 голосов
/ 22 февраля 2017

Пара получила в качестве дополнений к двум уже опубликованным исключительным ответам:

Доступ к полям набора записей в формах и отчетах
Элементом по умолчанию объектов формы в Access является объединение коллекции Controls формы и коллекции Fields набора записей формы. Если имя элемента управления конфликтует с именем поля, я не уверен, какой объект на самом деле возвращается. Поскольку свойство по умолчанию как поля, так и элемента управления равно их .Value, часто это «различие без разницы». Другими словами, обычно не волнует, что это потому, что значения поля и элемента управления часто совпадают.

Остерегайтесь конфликтов имен!
Эта ситуация усугубляется тем, что конструктор форм и отчетов Access по умолчанию именует связанные элементы управления так же, как поле набора записей, к которому они привязаны. Я лично принял соглашение о переименовании элементов управления с префиксом их типа (например, tbLastName для текстового поля, связанного с полем LastName ).

Поля набора записей отчета отсутствуют!
Ранее я уже говорил, что элемент по умолчанию для объекта Form представляет собой набор элементов управления и полей. Тем не менее, элемент по умолчанию объекта отчета является только его коллекция элементов управления. Поэтому, если кто-то хочет обратиться к полю набора записей, используя оператор взрыва, ему нужно включить это поле в качестве источника для (скрытого, при желании) элемента управления с привязкой.

Остерегайтесь конфликтов с явными свойствами формы / отчета
Когда кто-либо добавляет элементы управления в форму или отчет, Access автоматически создает свойства, которые ссылаются на эти элементы управления. Например, элемент управления с именем tbLastName будет доступен из модуля кода формы, ссылаясь на Me.tbLastName. Однако Access не будет создавать такое свойство, если оно конфликтует с существующей формой или свойством отчета. Например, предположим, что один добавляет элемент управления с именем Pages. Ссылка на Me.Pages в модуле кода формы возвращает свойство Pages формы, а не элемент управления с именем "Pages".

В этом примере можно получить доступ к элементу управления «Страницы» явно, используя Me.Controls("Pages") или неявно используя оператор взрыва, Me!Pages. Однако имейте в виду, что использование оператора взрыва означает, что Access может вместо этого вернуть поле с именем «Страницы», если оно существует в наборе записей формы.

А как. Значение?
Хотя эта тема не упоминается явно, эта тема возникла в приведенных выше комментариях. По умолчанию свойство для объектов поля и большинства «привязываемых к данным» ¹ управляющих объектов - .Value. Так как это свойство по умолчанию, обычно считается ненужным многословным всегда включать его явно. Таким образом, это стандартная практика:

Dim EmployeeLastName As String
EmployeeLastName = Me.tbLastName

Вместо:

EmployeeLastName = Me.tbLastName.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"

Можно ожидать, что приведенный выше код создает две записи в словаре 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, индексирующий словарь, а не значение элемента управления.

Мои персональные рекомендации / условные обозначения
За эти годы я принял следующие практики YMMV:

  • Префикс имени элемента формы / отчета с индикаторами типа элемента управления (например, tbTextBox, lblLabel и т. Д.)
  • См. Элементы управления формы / отчета в коде с использованием обозначения Me. (например, Me.tbLastName)
  • Избегайте создания полей таблицы / запроса с проблемными именами в первую очередь
  • Используйте обозначение Me! при возникновении конфликтов, например, с устаревшими приложениями (например, Me!Pages)
  • Включение скрытых элементов управления отчетом для получения доступа к значениям поля Recordset *
  • Явно включать .Value только в том случае, если ситуация требует добавленного многословия (например, словарные ключи)

¹ Что такое «привязываемый к данным» элемент управления?
По сути, элемент управления со свойством ControlSource, например TextBox или ComboBox. Не привязываемый элемент управления будет что-то вроде Label или CommandButton. Свойство по умолчанию для TextBox и ComboBox: .Value; Метки и кнопки CommandButton не имеют свойства по умолчанию.

...