Access
 sql >> база данни >  >> RDS >> Access

Функция за промяна на регистра на VBA

Като силен привърженик на контрола на версиите в Microsoft Access, трябва да говоря за най-голямата си забележка към средата за разработка на VBA:автоматично „преработване“ на идентификаторите. Мислете за това като за разширение на моя отговор на въпрос относно тази „функция“ на stackoverflow.

Ще разгледам тази статия в две части. В част 1 ще дефинирам поведението на средата за разработка. В част 2 ще обсъдя теорията си за това защо работи по този начин.

Част 1:Дефиниране на поведението

Ако сте прекарали известно време в писане на код във VBA, сигурен съм, че сте забелязали тази „функция“. Докато въвеждате идентификатори – променливи, имена на функции, изброявания и т.н. – може да забележите, че IDE автоматично променя главния и главния шрифт на тези идентификатори. Например, можете да напишете име на променлива с всички малки букви, но веднага щом преминете към нов ред, първата буква на вашата променлива  внезапно се превключва на главни.

Първият път, когато видите това, може да бъде разтърсващо. Докато продължавате да програмирате, IDE продължава да променя случая на вас привидно на случаен принцип. Но ако прекарате достатъчно време в IDE, в крайна сметка моделът се разкрива.

За да ви спестя от необходимостта да прекарате повече от десет години от живота си в очакване на модела да ви се разкрие, сега ще опиша модела така, както го разбирам. Доколкото ми е известно,  Microsoft никога не е документирал официално това поведение.

  1. Всички автоматични промени на регистъра са глобални за VBA проекта.
  2. Всеки път, когато декларационният ред на някой от следните типове идентификатори се промени, всеки друг идентификатор със същото име също се променя с главни букви:
    • Подиме
    • Име на функция
    • Въведете име
    • Име на Enum
    • Име на променлива
    • Постоянно име
    • Име на собственост
  3. Всеки път, когато името на изброимия елемент се промени някъде в кода, главният и главният ъгъл на името на изброяващия елемент се актуализира, за да съвпада навсякъде.

Нека поговорим за всяко от тези поведения сега малко по-подробно.

Глобални промени

Както писах по-горе, промените в случай на идентификатор са глобални за VBA проект. С други думи, VBA IDE напълно игнорира обхвата при промяна на главния и главния букви на идентификаторите.

Например, да приемем, че имате частна функция с име AccountIsActive в стандартен модул. Сега си представете модул за клас другаде в същия проект. Модулът клас има частна процедура за получаване на собственост. Вътре в тази процедура Property Get е локална променлива с име accountIsActive . Веднага след като въведете реда Dim accountIsActive As Boolean в VBA IDE и преминете към нов ред, функцията AccountIsActive който дефинирахме отделно в неговия собствен стандартен модул, редът за декларация е променен на Private Function accountIsActive() за да съответства на локалната променлива в този модул на класа.

Това е пълна хапка, така че нека го демонстрирам по-добре в код.

Стъпка 1:Дефинирайте функцията AccountIsActive

'--== Module1 ==--
Private Function AccountIsActive() As Boolean
End Function

Стъпка 2:Декларирайте локална променлива на accountIsActive в различен обхват

'--== Class1 ==--
Private Sub Foo()
    Dim accountIsACTIVE As Boolean
End Sub

Стъпка 3:VBA IDE...какво направи?!?!

'--== Module1 ==--
Private Function accountIsACTIVE() As Boolean
End Function

Правила за недискриминация на VBA Case-Obliteration

Не се задоволява просто да игнорира обхвата, VBA също пренебрегва разликите между видовете идентификатори в стремежа си да наложи последователност на главния регистър. С други думи, всеки път, когато декларирате нова функция, подпрограма или променлива, която използва съществуващо име на идентификатор, всички други екземпляри на този идентификатор се променят, за да съвпадат.

Във всеки от тези примери по-долу единственото нещо, което променям, е първият изброен модул. VBA IDE е отговорен за всички други промени в предварително дефинирани модули.

Стъпка 1:Дефинирайте функция

'--== Module1 ==--
Public Function ReloadDBData() As Boolean
End Function

Стъпка 2:Определете субстанция със същото име

ЗАБЕЛЕЖКА:Това е напълно валидно, стига процедурите да са в различни модули. Това каза, само защото *можете* да направите нещо, не означава, че *трябва*. И вие *трябва* да избягвате тази ситуация, ако е възможно.

'--== Module2 ==--
Public Sub ReloadDbData()
End Sub

'--== Module1 ==--
Public Function ReloadDbData() As Boolean
End Sub

Стъпка 3:Дефинирайте тип със същото име

ЗАБЕЛЕЖКА:Отново, моля, не дефинирайте под, функция и въведете всички с едно и също име в един проект.

'--== Module3 ==--
Private Type ReLoadDBData
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub ReLoadDBData()
End Sub

'--== Module1 ==--
Public Function ReLoadDBData() As Boolean
End Sub

Стъпка 4:Дефинирайте изброяване със същото име

ЗАБЕЛЕЖКА:Моля, моля, моля, за любовта към всичко свято...

'--== Module4 ==--
Public Enum ReloadDbDATA
    Dummy
End Enum

'--== Module3 ==--
Private Type ReloadDbDATA
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub ReloadDbDATA()
End Sub

'--== Module1 ==--
Public Function ReloadDbDATA() As Boolean
End Sub

Стъпка 5:Дефинирайте променлива със същото име

ЗАБЕЛЕЖКА:Всъщност все още правим това?

'--== Module5 ==--
Public reloaddbdata As Boolean

'--== Module4 ==--
Public Enum reloaddbdata
    Dummy
End Enum

'--== Module3 ==--
Private Type reloaddbdata
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub reloaddbdata()
End Sub

'--== Module1 ==--
Public Function reloaddbdata() As Boolean
End Sub

Стъпка 6:Дефинирайте константа със същото име

ЗАБЕЛЕЖКА:О, хайде. Сериозно?

'--== Module6 ==--
Private Const RELOADDBDATA As Boolean = True

'--== Module5 ==--
Public RELOADDBDATA As Boolean

'--== Module4 ==--
Public Enum RELOADDBDATA
    Dummy
End Enum

'--== Module3 ==--
Private Type RELOADDBDATA
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub RELOADDBDATA()
End Sub

'--== Module1 ==--
Public Function RELOADDBDATA() As Boolean
End Sub

Стъпка 7:Дефинирайте свойство на клас със същото име

ЗАБЕЛЕЖКА:Това става глупаво.

'--== Class1 ==--
Private Property Get reloadDBData() As Boolean
End Property

'--== Module6 ==--
Private Const reloadDBData As Boolean = True

'--== Module5 ==--
Public reloadDBData As Boolean

'--== Module4 ==--
Public Enum reloadDBData
    Dummy
End Enum

'--== Module3 ==--
Private Type reloadDBData
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub reloadDBData()
End Sub

'--== Module1 ==--
Public Function reloadDBData() As Boolean
End Sub

Enum елементи?!?!

За тази трета точка е важно да се прави разлика между Enum тип и Елемент Enum .

Enum EnumTypeName   ' <-- Enum type
    EnumItemAlice   ' <-- Enum item
    EnumItemBob     ' <-- Enum item
End Enum

Вече показахме по-горе, че типовете Enum се третират по същия начин като другите видове декларации, като подменници, функции, константи и променливи. Всеки път, когато декларационният ред за идентификатор с това име се промени, всеки друг идентификатор в проекта със същото име има своя главни букви актуализиран, за да съответства на последната промяна.

Изброяване на елементи са специални по това, че са единственият вид идентификатор, чийто корпус може да се променя всеки път, когато който и да е ред код който съдържа името на изброяващия елемент се променя.

Стъпка 1. Дефинирайте и попълнете Enum

'--== Module7 ==--
Public Enum EnumTypeName
    EnumItemAlice
    EnumItemBob
End Enum

Стъпка 2. Обърнете се към елементите Enum в кода

'--== Module8 ==--
Sub TestEnum()
    Debug.Print EnumItemALICE, EnumItemBOB
End Sub

Резултат:Промените в декларацията на типа Enum, за да съответстват на обикновения ред код

'--== Module7 ==--
Public Enum EnumTypeName
    EnumItemALICE
    EnumItemBOB
End Enum

Част 2:Как стигнахме до тук?

Никога не съм говорил с никого от вътрешния екип за разработка на VBA. Никога не съм виждал официална документация защо VBA IDE работи по начина, по който работи. Така че това, което ще напиша, е чисто предположение, но мисля, че има някакъв смисъл.

Дълго време се чудех защо в света VBA IDE ще има това поведение. В крайна сметка явно е умишлено. Най-лесното нещо за IDE би било...нищо. Ако потребителят декларира променлива с главни букви, оставете я да стои с всички главни. Ако след това потребителят препраща тази променлива с малки букви няколко реда по-късно, оставете тази препратка с малки букви и оригиналната декларация с главни букви.

Това би било напълно приемливо изпълнение на езика VBA. В крайна сметка самият език не е чувствителен към малки и големи букви. И така, защо да се мъчите автоматично да променяте главния идентификатор?

По ирония на съдбата, вярвам, че мотивацията беше да се избегне объркване. (Замах и пропуск, ако питате мен.)  Присмивам се на това обяснение, но има някакъв смисъл.

Контраст с езици, чувствителни към главни и малки букви

Първо, нека поговорим за програмисти, които идват от език, чувствителен към малки и големи букви. Често срещана конвенция в езиците, чувствителни към малки и малки букви, като C#, е да се наименуват обекти на класа с главни букви и да се назовават екземпляри на тези обекти със същото име като класа, но с водеща малка буква.

Тази конвенция няма да работи във VBA, тъй като два идентификатора, които се различават само по главни и главни букви, се считат за еквивалентни. Всъщност, Office VBA IDE няма да ви позволи едновременно да декларирате функция с един тип корпус и локална променлива с различен вид корпус (покрихме това изчерпателно по-горе). Това не позволява на разработчика да приеме, че има семантична разлика между два идентификатора с едни и същи букви, но различни главни букви.

Грешен код да изглежда грешен

По-вероятното обяснение според мен е, че тази „функция“ съществува, за да направи еквивалентните идентификатори да изглеждат идентични. Помисли за това; без тази функция би било лесно печатните грешки да се превърнат в грешки по време на изпълнение. Не ми вярвате? Помислете за това:

Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"

Private Sub Class_Initialize()
    mAccountName = ACCOUNT_NAME
End Sub

Public Property Get MyAccountName() As String
    MAccountName = Account_Name
End Property

Public Property Let MyAccountName(AccountName As String)
    mAccountName = Account_Name
End Property

Ако погледнете бързо горния код, той изглежда доста ясен. Това е клас с .MyAccountName Имот. Променливата-член за свойството се инициализира до константна стойност, когато обектът е създаден. Когато зададете името на акаунта в код, променливата на члена отново се актуализира. Когато извлича стойността на свойството, кодът просто връща съдържанието на променливата-член.

Поне това трябва да прави. Ако копирам горния код и го поставя в прозорец на VBA IDE, корпусът на идентификаторите става последователен и грешките по време на изпълнение изведнъж се показват:

Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"

Private Sub Class_Initialize()
    mAccountName = ACCOUNT_NAME   ' <- This is OK
End Sub

Public Property Get MyAccountName() As String
    mAccountName = ACCOUNT_NAME   ' <- This is probably not what we intended
End Property

Public Property Let MyAccountName(AccountName As String)
    mAccountName = ACCOUNT_NAME   ' <- This is definitely not what we meant
End Property

Внедряване:Това наистина ли е най-добрият подход?

Ммм, не. Не ме разбирайте погрешно. Всъщност наистина харесвам идеята за автоматична промяна на главните букви на идентификаторите, за да поддържам последователност. Единственото ми истинско оплакване е, че промяната се прави на всеки идентификатор с това име в целия проект. Много по-добре би било да промените главните букви само на онези идентификатори, които се отнасят до едно и също „нещо“ (независимо дали това „нещо“ е функция, под, свойство, променлива и т.н.).

Така че защо не работи по този начин? Очаквам, че разработчиците на VBA IDE са съгласни с моята гледна точка за това как трябва да работи. Но има много основателна причина защо IDE не работи по този начин. С една дума, производителност.

За съжаление има само един надежден начин да откриете кои идентификатори със същото име всъщност се отнасят за едно и също нещо:анализирайте всеки ред от код. Това е бавнооооооо. Това е нещо повече от проста хипотеза от моя страна. Проектът Rubberduck VBA всъщност прави точно това; той анализира всеки ред код в проекта, за да може да прави автоматизиран анализ на кода и куп други страхотни неща.

Проектът несъмнено е тежък. Вероятно работи чудесно за проекти на Excel. За съжаление, никога не съм бил достатъчно търпелив, за да го използвам в някой от проектите си за Access. Rubberduck VBA е технически впечатляващ проект, но също така е и предупредителна приказка. Зачитането на обхвата при промяна на главни букви за идентификатори би било хубаво да има, но не за сметка на текущата невероятно бърза производителност на VBA IDE.

Последни мисли

Разбирам мотивацията за тази функция. Мисля, че дори разбирам защо се прилага така, както е. Но това е единствената най-влудяваща странност на VBA за мен.

Ако можех да дам една-единствена препоръка към екипа за разработка на Office VBA, това би било да предложа настройка в IDE за деактивиране на автоматичните промени на случаите. Текущото поведение може да остане активирано по подразбиране. Но за опитни потребители, които се опитват да се интегрират със системи за контрол на версиите, поведението може да бъде напълно деактивирано, за да се предотврати неприятните „промени в кода“ от замърсяване на историята на ревизиите.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Колко потребители имат достъп до поддръжка?

  2. Как да отстраните тези 3 често срещани проблема с достъпа

  3. Съвети за база данни за начинаещи

  4. Как да използвате съветника за кръстосани заявки в Access

  5. Актуален ли е Microsoft Access през 2020 г.?