Là một người ủng hộ mạnh mẽ việc kiểm soát phiên bản trong Microsoft Access, tôi cần nói về điểm hấp dẫn lớn nhất của tôi với môi trường phát triển VBA:tự động "rút lại" các số nhận dạng. Hãy coi đây là phần mở rộng câu trả lời của tôi cho câu hỏi về "tính năng" này trên stackoverflow.
Tôi sẽ tiếp cận bài viết này trong hai phần. Trong Phần 1, tôi sẽ định nghĩa hành vi của môi trường phát triển. Trong Phần 2, tôi sẽ thảo luận về lý thuyết của tôi về lý do tại sao nó hoạt động theo cách này.
Phần 1:Xác định hành vi
Nếu bạn đã dành bất kỳ khoảng thời gian nào để viết mã trong VBA, tôi chắc chắn rằng bạn đã nhận thấy "tính năng" này. Khi bạn đang nhập số nhận dạng — biến, tên hàm, enums, v.v. — bạn có thể nhận thấy rằng IDE tự động thay đổi cách viết hoa của các số nhận dạng này. Ví dụ:bạn có thể gõ một tên biến bằng tất cả các chữ cái viết thường, nhưng ngay sau khi bạn chuyển sang một dòng mới, chữ cái đầu tiên của biến của bạn đột ngột chuyển sang chữ hoa.
Lần đầu tiên bạn nhìn thấy điều này, nó có thể bị chói tai. Khi bạn tiếp tục lập trình, IDE tiếp tục thay đổi trường hợp trên bạn dường như một cách ngẫu nhiên. Tuy nhiên, nếu bạn dành đủ thời gian trong IDE, cuối cùng thì mô hình sẽ tự xuất hiện.
Để giúp bạn không phải dành hơn mười năm cuộc đời để chờ đợi hình mẫu tự tiết lộ cho bạn, bây giờ tôi sẽ mô tả mô hình đó như tôi đã hiểu về nó. Theo hiểu biết của tôi, Microsoft chưa bao giờ chính thức ghi lại bất kỳ hành vi nào về hành vi này.
- Tất cả các thay đổi trường hợp tự động là toàn cầu đối với dự án VBA.
- Bất cứ khi nào dòng khai báo của bất kỳ loại số nhận dạng nào sau đây được thay đổi, mọi số nhận dạng khác có cùng tên cũng sẽ thay đổi cách viết hoa:
- Tên phụ
- Tên hàm
- Nhập tên
- Tên đăng ký
- Tên biến
- Tên không đổi
- Tên tài sản
- Bất cứ khi nào tên mục enum được thay đổi ở bất kỳ đâu trong mã, cách viết hoa của tên mục enum sẽ được cập nhật để khớp ở mọi nơi.
Bây giờ, hãy nói chi tiết hơn về từng hành vi này.
Thay đổi toàn cầu
Như tôi đã viết ở trên, các thay đổi trường hợp số nhận dạng là toàn cầu đối với một dự án VBA. Nói cách khác, VBA IDE hoàn toàn bỏ qua phạm vi khi thay đổi trường hợp của số nhận dạng.
Ví dụ:giả sử bạn có một chức năng riêng tư có tên AccountIsActive trong một mô-đun tiêu chuẩn. Bây giờ, hãy tưởng tượng một mô-đun lớp ở nơi khác trong cùng dự án đó. Mô-đun lớp có một thủ tục Nhận thuộc tính riêng. Bên trong thủ tục Nhận thuộc tính đó là một biến cục bộ có tên accountIsActive . Ngay sau khi bạn gõ dòng Dim accountIsActive As Boolean
vào VBA IDE và chuyển sang một dòng mới, hàm AccountIsActive mà chúng tôi đã xác định riêng trong mô-đun tiêu chuẩn của riêng nó đã thay đổi dòng khai báo thành Private Function accountIsActive()
để khớp với biến cục bộ bên trong mô-đun lớp này.
Đó là một điều thú vị, vì vậy hãy để tôi chứng minh nó tốt hơn trong mã.
Bước 1:Xác định chức năng AccountIsActive
'--== Module1 ==--
Private Function AccountIsActive() As Boolean
End Function
Bước 2:Khai báo biến cục bộ accountIsActive trong phạm vi khác nhau
'--== Class1 ==--
Private Sub Foo()
Dim accountIsACTIVE As Boolean
End Sub
Bước 3:VBA IDE ... bạn đã làm gì?!?!
'--== Module1 ==--
Private Function accountIsACTIVE() As Boolean
End Function
Chính sách Không phân biệt đối xử của VBA Case-Obliteration
Không chỉ đơn giản là bỏ qua phạm vi, VBA cũng bỏ qua sự khác biệt giữa các loại mã định danh trong nhiệm vụ áp đặt tính nhất quán của cách viết hoa. Nói cách khác, mỗi khi bạn khai báo một hàm, chương trình con hoặc biến mới sử dụng tên mã định danh hiện có, tất cả các trường hợp khác của mã định danh đó sẽ được thay đổi trường hợp của chúng để phù hợp.
Trong mỗi ví dụ dưới đây, điều duy nhất tôi đang thay đổi là mô-đun đầu tiên được liệt kê. VBA IDE chịu trách nhiệm về tất cả các thay đổi khác đối với các mô-đun đã xác định trước đó.
Bước 1:Xác định một chức năng
'--== Module1 ==--
Public Function ReloadDBData() As Boolean
End Function
Bước 2:Xác định một phụ có cùng tên
LƯU Ý:Điều này hoàn toàn hợp lệ miễn là các thủ tục nằm trong các mô-đun khác nhau. Điều đó nói rằng, chỉ vì bạn * có thể * làm điều gì đó, không có nghĩa là bạn * nên *. Và bạn * nên * tránh tình huống này nếu có thể.
'--== Module2 ==--
Public Sub ReloadDbData()
End Sub
'--== Module1 ==--
Public Function ReloadDbData() As Boolean
End Sub
Bước 3:Xác định một loại có cùng tên
LƯU Ý:Xin nhắc lại, vui lòng không xác định một chức năng con, và nhập tất cả có cùng tên vào một dự án.
'--== Module3 ==--
Private Type ReLoadDBData
Dummy As Variant
End Type
'--== Module2 ==--
Public Sub ReLoadDBData()
End Sub
'--== Module1 ==--
Public Function ReLoadDBData() As Boolean
End Sub
Bước 4:Xác định một enum có cùng tên
LƯU Ý:Làm ơn, làm ơn, làm ơn, vì tình yêu của mọi vật thánh thiện ...
'--== 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
Bước 5:Xác định biến có cùng tên
LƯU Ý:Chúng tôi thực sự vẫn đang làm điều này?
'--== 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
Bước 6:Xác định một hằng số có cùng tên
LƯU Ý:Ồ, thôi. Nghiêm túc?
'--== 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
Bước 7:Xác định thuộc tính lớp có cùng tên
LƯU Ý:Điều này đang trở nên ngớ ngẩn.
'--== 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
Mục Enum?!?!
Đối với điểm thứ ba này, điều quan trọng là phải phân biệt giữa một Loại Enum và một Enum item .
Enum EnumTypeName ' <-- Enum type
EnumItemAlice ' <-- Enum item
EnumItemBob ' <-- Enum item
End Enum
Ở trên chúng ta đã chỉ ra rằng các kiểu Enum được xử lý giống như các kiểu khai báo khác, như con, hàm, hằng và biến. Bất cứ khi nào dòng khai báo cho một số nhận dạng có tên đó được thay đổi, mọi số nhận dạng khác trong dự án có cùng tên sẽ được cập nhật cách viết hoa của nó để phù hợp với thay đổi mới nhất.
Enum mục đặc biệt ở chỗ chúng là loại số nhận dạng duy nhất có thể thay đổi cách viết hoa bất cứ khi nào bất kỳ dòng mã nào có chứa tên mục enum được thay đổi.
Bước 1. Xác định và điền Enum
'--== Module7 ==--
Public Enum EnumTypeName
EnumItemAlice
EnumItemBob
End Enum
Bước 2. Tham khảo các mục Enum trong mã
'--== Module8 ==--
Sub TestEnum()
Debug.Print EnumItemALICE, EnumItemBOB
End Sub
Kết quả:Khai báo loại danh sách thay đổi để phù hợp với dòng mã thông thường
'--== Module7 ==--
Public Enum EnumTypeName
EnumItemALICE
EnumItemBOB
End Enum
Phần 2:Chúng tôi đến đây bằng cách nào?
Tôi chưa bao giờ nói chuyện với bất kỳ ai trong nhóm phát triển VBA nội bộ. Tôi chưa bao giờ thấy bất kỳ tài liệu chính thức nào về lý do tại sao VBA IDE hoạt động theo cách của nó. Vì vậy, những gì tôi sắp viết chỉ là phỏng đoán thuần túy, nhưng tôi nghĩ nó có lý.
Trong một thời gian dài, tôi tự hỏi tại sao trên thế giới VBA IDE lại có hành vi này. Rốt cuộc, nó rõ ràng là cố ý. Điều dễ dàng nhất đối với IDE sẽ là ... không có gì. Nếu người dùng khai báo một biến bằng chữ hoa toàn bộ, hãy viết hoa toàn bộ biến đó. Nếu người dùng sau đó tham chiếu đến biến đó bằng chữ thường sau một vài dòng, hãy để tham chiếu đó bằng chữ thường và khai báo ban đầu được viết hoa toàn bộ.
Đây sẽ là một triển khai hoàn toàn có thể chấp nhận được của ngôn ngữ VBA. Xét cho cùng, bản thân ngôn ngữ không phân biệt chữ hoa chữ thường. Vì vậy, tại sao lại gặp khó khăn khi tự động thay đổi cách viết hoa của số nhận dạng?
Trớ trêu thay, tôi tin rằng động cơ là để tránh nhầm lẫn. (Đánh đu và bỏ lỡ, nếu bạn hỏi tôi.) Tôi chế giễu lời giải thích này, nhưng nó cũng có lý.
Tương phản với các ngôn ngữ phân biệt chữ hoa chữ thường
Đầu tiên, hãy nói về các lập trình viên đến từ một ngôn ngữ phân biệt chữ hoa chữ thường. Quy ước chung trong các ngôn ngữ phân biệt chữ hoa chữ thường, chẳng hạn như C #, là đặt tên cho các đối tượng của lớp bằng chữ in hoa và đặt tên cho các phiên bản của các đối tượng đó cùng tên với lớp nhưng bằng chữ thường ở đầu.
Quy ước đó sẽ không hoạt động trong VBA, vì hai số nhận dạng chỉ khác nhau về cách viết hoa được coi là tương đương. Trên thực tế, Office VBA IDE sẽ không cho phép bạn khai báo đồng thời một hàm với một kiểu viết hoa và một biến cục bộ với một kiểu viết hoa khác (chúng tôi đã đề cập đến điều này ở trên). Điều này ngăn nhà phát triển giả định rằng có sự khác biệt về ngữ nghĩa giữa hai số nhận dạng có cùng các chữ cái nhưng khác cách viết hoa.
Làm sai mã trông sai
Lời giải thích dễ hiểu hơn trong tâm trí tôi là "tính năng" này tồn tại để làm cho các số nhận dạng tương đương trông giống hệt nhau. Hãy suy nghĩ về nó; nếu không có tính năng này, sẽ rất dễ dẫn đến lỗi chính tả dẫn đến lỗi thời gian chạy. Không tin tôi? Hãy xem xét điều này:
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
Nếu bạn lướt nhanh qua đoạn mã trên, nó trông khá đơn giản. Đó là một lớp học có. MyAccountName bất động sản. Biến thành viên cho thuộc tính được khởi tạo thành một giá trị không đổi khi đối tượng được tạo. Khi đặt tên tài khoản trong mã, biến thành viên lại được cập nhật. Khi truy xuất giá trị thuộc tính, mã chỉ trả về nội dung của biến thành viên.
Ít nhất, đó là những gì nó phải làm. Nếu tôi sao chép mã ở trên và dán vào cửa sổ VBA IDE, cách viết hoa của số nhận dạng trở nên nhất quán và lỗi thời gian chạy đột nhiên tự hiển thị:
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
Triển khai:Đây có thực sự là cách tiếp cận tốt nhất không?
Ừm ... không. Đừng hiểu sai ý tôi. Tôi thực sự rất thích ý tưởng tự động thay đổi cách viết hoa của số nhận dạng để duy trì tính nhất quán. Mối quan tâm thực sự duy nhất của tôi là sự thay đổi được thực hiện đối với mọi định danh có tên đó trong toàn bộ dự án. Tốt hơn hết là chỉ nên thay đổi cách viết hoa của những từ định danh tham chiếu đến cùng một "thứ" (cho dù "thứ" đó là một hàm, con, thuộc tính, biến, v.v.).
Vậy tại sao nó không hoạt động theo cách này? Tôi hy vọng các nhà phát triển VBA IDE đồng ý với quan điểm của tôi về cách nó hoạt động. Nhưng, có một lý do rất chính đáng tại sao IDE không hoạt động theo cách đó. Nói một cách ngắn gọn, hiệu suất.
Thật không may, chỉ có một cách đáng tin cậy để phát hiện ra số nhận dạng nào có cùng tên thực sự tham chiếu đến cùng một thứ:phân tích cú pháp mọi dòng mã. Đó là sloooowwwww. Về phần tôi, đây không chỉ là một giả thuyết đơn giản. Dự án Rubberduck VBA thực sự thực hiện chính xác điều này; nó phân tích cú pháp mọi dòng mã trong dự án để có thể thực hiện phân tích mã tự động và một loạt các nội dung thú vị khác.
Dự án được thừa nhận là nặng ký. Nó có thể hoạt động tốt cho các dự án Excel. Thật không may, tôi chưa bao giờ đủ kiên nhẫn để sử dụng nó trong bất kỳ dự án Access nào của mình. Rubberduck VBA là một dự án ấn tượng về mặt kỹ thuật, nhưng nó cũng là một câu chuyện cảnh giác. Việc tôn trọng phạm vi khi thay đổi cách viết hoa cho các số nhận dạng là điều rất nên làm, nhưng không làm mất đi hiệu suất cực nhanh hiện tại của VBA IDE.
Kết luận
Tôi hiểu động lực cho tính năng này. Tôi nghĩ rằng tôi thậm chí còn hiểu tại sao nó được triển khai như vậy. Nhưng đó là câu nói ngớ ngẩn nhất của VBA đối với tôi.
Nếu tôi có thể đưa ra một đề xuất cho nhóm phát triển Office VBA, thì đó sẽ là đưa ra một cài đặt trong IDE để tắt các thay đổi trường hợp tự động. Hành vi hiện tại có thể vẫn được bật theo mặc định. Tuy nhiên, đối với những người dùng thành thạo đang cố gắng tích hợp với hệ thống kiểm soát phiên bản, hành vi này có thể bị vô hiệu hóa hoàn toàn để ngăn những "thay đổi mã" phiền toái làm ô nhiễm lịch sử sửa đổi.