Access
 sql >> Baza danych >  >> RDS >> Access

Funkcja zmiany przypadku VBA

Jako zagorzały zwolennik kontroli wersji w Microsoft Access, muszę opowiedzieć o moim największym problemie związanym ze środowiskiem programistycznym VBA:automatycznym „przemienianiu” identyfikatorów. Pomyśl o tym jako o rozwinięciu mojej odpowiedzi na pytanie dotyczące tej „funkcji” na stackoverflow.

Zamierzam podejść do tego artykułu w dwóch częściach. W części 1 zdefiniuję zachowanie środowiska programistycznego. W części 2 omówię moją teorię, dlaczego to działa w ten sposób.

Część 1:Definiowanie zachowania

Jeśli spędziłeś trochę czasu na pisaniu kodu w VBA, jestem pewien, że zauważyłeś tę „funkcję”. Podczas wpisywania identyfikatorów — zmiennych, nazw funkcji, wyliczeń itp. — możesz zauważyć, że IDE automatycznie zmienia wielkość liter tych identyfikatorów. Na przykład możesz wpisać nazwę zmiennej wszystkimi małymi literami, ale gdy tylko przejdziesz do nowego wiersza, pierwsza litera zmiennej nagle zmieni się na wielką.

Gdy widzisz to po raz pierwszy, może to być wstrząsające. Gdy kontynuujesz programowanie, IDE kontynuuje zmianę przypadku pozornie losowo. Ale jeśli spędzisz wystarczająco dużo czasu w IDE, w końcu wzór się ujawni.

Aby uchronić cię przed koniecznością spędzenia ponad dziesięciu lat swojego życia w oczekiwaniu na ujawnienie się wzoru, opiszę teraz wzór tak, jak go zrozumiałem. O ile mi wiadomo, firma Microsoft nigdy oficjalnie nie udokumentowała żadnego z takich zachowań.

  1. Wszystkie automatyczne zmiany wielkości liter są globalne dla projektu VBA.
  2. Za każdym razem, gdy zmieniany jest wiersz deklaracji dowolnego z poniższych typów identyfikatorów, zmieniany jest również każdy inny identyfikator o tej samej nazwie:
    • Nazwa podrzędna
    • Nazwa funkcji
    • Wpisz nazwę
    • Nazwa wyliczenia
    • Nazwa zmiennej
    • Nazwa stała
    • Nazwa nieruchomości
  3. Za każdym razem, gdy nazwa elementu wyliczenia jest zmieniana gdziekolwiek w kodzie, wielkość liter nazwy elementu wyliczenia jest aktualizowana, aby pasowała wszędzie.

Porozmawiajmy teraz o każdym z tych zachowań bardziej szczegółowo.

Zmiany globalne

Jak napisałem powyżej, zmiany wielkości identyfikatorów są globalne dla projektu VBA. Innymi słowy, VBA IDE całkowicie ignoruje zakres podczas zmiany wielkości liter w identyfikatorach.

Załóżmy na przykład, że masz prywatną funkcję o nazwie KontoIsActive w standardowym module. Teraz wyobraź sobie moduł klasy w innym miejscu tego samego projektu. Moduł klasy posiada prywatną procedurę Property Get. Wewnątrz tej procedury pobierania właściwości znajduje się zmienna lokalna o nazwie accountIsActive . Jak tylko wpiszesz wiersz Dim accountIsActive As Boolean do środowiska IDE VBA i przejdź do nowej linii, funkcja Konto jest aktywne który zdefiniowaliśmy osobno w jego własnym standardowym module ma swoją linię deklaracji zmienioną na Private Function accountIsActive() aby dopasować lokalną zmienną wewnątrz tego modułu klasy.

To kęs, więc pozwól, że zademonstruję to lepiej w kodzie.

Krok 1:Zdefiniuj funkcję KontoIsAktywne

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

Krok 2:zadeklaruj, że zmienna lokalna konto jest aktywna w innym zakresie

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

Krok 3:IDE VBA... co zrobiłeś?!?!

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

Zasady niedyskryminacji VBA Case-Obliteration

Nie zadowalając się po prostu ignorowaniem zakresu, VBA ignoruje również różnice między rodzajami identyfikatorów w swoim dążeniu do narzucenia spójności wielkości liter. Innymi słowy, za każdym razem, gdy deklarujesz nową funkcję, podprogram lub zmienną, która używa istniejącej nazwy identyfikatora, wszystkie inne wystąpienia tego identyfikatora mają zmienioną wielkość liter w celu dopasowania.

W każdym z poniższych przykładów jedyną rzeczą, którą zmieniam, jest pierwszy wymieniony moduł. IDE VBA jest odpowiedzialne za wszystkie inne zmiany we wcześniej zdefiniowanych modułach.

Krok 1:Zdefiniuj funkcję

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

Krok 2:Zdefiniuj sub o tej samej nazwie

UWAGA:Jest to całkowicie ważne, o ile procedury są w różnych modułach. To powiedziawszy, tylko dlatego, że *możesz* coś zrobić, nie oznacza, że ​​*powinieneś*. I *powinnaś* unikać takiej sytuacji, jeśli to w ogóle możliwe.

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

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

Krok 3:Zdefiniuj typ o tej samej nazwie

UWAGA:Ponownie, proszę nie definiować podrzędnej, funkcji i wpisywać wszystkich o tej samej nazwie w jednym projekcie.

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

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

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

Krok 4:Zdefiniuj wyliczenie o tej samej nazwie

UWAGA:Proszę, proszę, proszę, z miłości do wszystkiego co święte...

'--== 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

Krok 5:Zdefiniuj zmienną o tej samej nazwie

UWAGA:nadal to robimy?

'--== 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

Krok 6:Zdefiniuj stałą o tej samej nazwie

UWAGA:Och, daj spokój. Poważnie?

'--== 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

Krok 7:Zdefiniuj właściwość klasy o tej samej nazwie

UWAGA:to się robi głupie.

'--== 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

Wyliczenie przedmiotów?!?!

W tym trzecim punkcie ważne jest, aby rozróżnić typ wyliczenia i Enumeracja .

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

Pokazaliśmy już powyżej, że typy Enum są traktowane tak samo, jak inne rodzaje deklaracji, takie jak subs, funkcje, stałe i zmienne. Za każdym razem, gdy zmienia się wiersz deklaracji dla identyfikatora o tej nazwie, każdy inny identyfikator w projekcie o tej samej nazwie ma zaktualizowaną wielkość liter, aby pasował do ostatniej zmiany.

Wyliczenie przedmiotów są wyjątkowe, ponieważ są jedynym rodzajem identyfikatora, którego wielkość liter może zostać zmieniona za każdym razem, gdy dowolna linia kodu który zawiera nazwę elementu wyliczenia jest zmieniana.

Krok 1. Zdefiniuj i wypełnij Enum

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

Krok 2. Zapoznaj się z pozycjami Wyliczenie w kodzie

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

Wynik:zmiana deklaracji typu wyliczenia w celu dopasowania do zwykłego wiersza kodu

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

Część 2:Jak się tu dostaliśmy?

Nigdy nie rozmawiałem z nikim z wewnętrznego zespołu programistów VBA. Nigdy nie widziałem żadnej oficjalnej dokumentacji wyjaśniającej, dlaczego IDE VBA działa tak, jak działa. Więc to, co zamierzam napisać, jest czystym przypuszczeniem, ale myślę, że ma to jakiś sens.

Przez długi czas zastanawiałem się, dlaczego na świecie IDE VBA miałoby takie zachowanie. W końcu jest to wyraźnie zamierzone. Najłatwiejszą rzeczą do zrobienia dla IDE byłoby... nic. Jeśli użytkownik deklaruje zmienną wielkimi literami, niech będzie pisana wielkimi literami. Jeśli użytkownik odwołuje się do tej zmiennej kilka wierszy później małymi literami, zostaw to odwołanie małymi literami, a oryginalną deklarację wielkimi literami.

Byłaby to całkowicie akceptowalna implementacja języka VBA. W końcu sam język nie rozróżnia wielkości liter. Po co więc zawracać sobie głowę automatyczną zmianą wielkości liter identyfikatora?

Jak na ironię, uważam, że motywacją było uniknięcie zamieszania. (Swing i pudło, jeśli mnie pytasz.) Kpię z tego wyjaśnienia, ale ma to jakiś sens.

Kontrast z językami, w których rozróżniana jest wielkość liter

Najpierw porozmawiajmy o programistach wywodzących się z języka rozróżniającego wielkość liter. Powszechną konwencją w językach rozróżniających wielkość liter, takich jak C#, jest nazywanie obiektów klas wielkimi literami i nazywanie wystąpień tych obiektów taką samą nazwą jak klasa, ale z wiodącą małą literą.

Ta konwencja nie zadziała w VBA, ponieważ dwa identyfikatory, które różnią się tylko wielkością liter, są uważane za równoważne. W rzeczywistości Office VBA IDE nie pozwoli Ci jednocześnie zadeklarować funkcji z jednym typem wielkości liter i lokalnej zmiennej z innym rodzajem wielkości liter (omówiliśmy to dokładnie powyżej). Uniemożliwia to programiście założenie, że istnieje różnica semantyczna między dwoma identyfikatorami z tymi samymi literami, ale różnymi wielkościami liter.

Niewłaściwy kod wygląda źle

Bardziej prawdopodobnym wyjaśnieniem w moim umyśle jest to, że ta „funkcja” istnieje po to, aby równoważne identyfikatory wyglądały identycznie. Pomyśl o tym; bez tej funkcji literówki mogłyby łatwo przekształcić się w błędy w czasie wykonywania. Nie wierzysz mi? Rozważ to:

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

Jeśli spojrzysz szybko na powyższy kod, wygląda to całkiem prosto. To klasa z rozszerzeniem .MyAccountName własność. Zmienna członkowska dla właściwości jest inicjowana do wartości stałej podczas tworzenia obiektu. Podczas ustawiania nazwy konta w kodzie, zmienna składowa jest ponownie aktualizowana. Podczas pobierania wartości właściwości kod jedynie zwraca zawartość zmiennej składowej.

Przynajmniej tak ma robić. Jeśli skopiuję powyższy kod i wkleję go do okna VBA IDE, wielkość liter identyfikatorów stanie się spójna i nagle pojawią się błędy uruchomieniowe:

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

Wdrożenie:czy to naprawdę najlepsze podejście?

Umm nie. Nie zrozum mnie źle. Właściwie bardzo podoba mi się pomysł automatycznej zmiany wielkości liter w identyfikatorach, aby zachować spójność. Moim jedynym prawdziwym problemem jest to, że zmiana dotyczy każdego identyfikatora o tej nazwie w całym projekcie. Znacznie lepiej byłoby zmienić wielkość liter tylko w tych identyfikatorach, które odnoszą się do tej samej „rzeczy” (niezależnie od tego, czy „rzecz” jest funkcją, podrzędną, właściwością, zmienną itp.).

Więc dlaczego to nie działa w ten sposób? Oczekuję, że programiści VBA IDE zgadzają się z moim zdaniem, jak to powinno działać. Ale jest bardzo dobry powód dlaczego IDE nie działa w ten sposób. Jednym słowem wydajność.

Niestety, istnieje tylko jeden niezawodny sposób na odkrycie, które identyfikatory o tej samej nazwie faktycznie odnoszą się do tej samej rzeczy:przeanalizuj każdy wiersz kodu. To jest sloooowwww. To więcej niż prosta hipoteza z mojej strony. Projekt Rubberduck VBA faktycznie robi dokładnie to; analizuje każdy wiersz kodu w projekcie, dzięki czemu może przeprowadzić zautomatyzowaną analizę kodu i kilka innych fajnych rzeczy.

Projekt jest wprawdzie ciężki. Prawdopodobnie działa świetnie w projektach Excel. Niestety, nigdy nie byłem na tyle cierpliwy, aby używać go w żadnym z moich projektów Access. Rubberduck VBA to imponujący technicznie projekt, ale to także przestroga. Respektowanie zakresu przy zmianie wielkości liter w identyfikatorach byłoby dobrze mieć, ale nie kosztem obecnej, niesamowicie szybkiej wydajności VBA IDE.

Końcowe myśli

Rozumiem motywację tej funkcji. Myślę, że nawet rozumiem, dlaczego jest to tak zaimplementowane. Ale to dla mnie najbardziej irytujące dziwactwo VBA.

Gdybym mógł przekazać jedną rekomendację zespołowi programistów Office VBA, byłoby to zaoferowanie ustawienia w IDE, aby wyłączyć automatyczne zmiany wielkości liter. Bieżące zachowanie może pozostać domyślnie włączone. Jednak w przypadku zaawansowanych użytkowników, którzy próbują zintegrować się z systemami kontroli wersji, zachowanie to może zostać całkowicie wyłączone, aby zapobiec zanieczyszczaniu historii wersji przez uciążliwe „zmiany kodu”.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Historia baz danych

  2. Rozszyfrowywanie nowych sterowników ODBC i OLEDB Microsoft SQL Server

  3. Zorganizuj swoje domowe biuro w celu zwiększenia produktywności

  4. Jak utworzyć formularz z tabeli w programie Access 2016

  5. Dostęp ODBC z Windows Server Core