Để hiểu biến "toàn cục", cần phải có 2 khái niệm. Thứ nhất là về bộ nhớ, và thứ hai là sự "nhìn thấy".
Nói cách khác, biến được chứa trong góc nào của bộ nhớ, và làm cách nào để truy cập biến (điển hình, bị che khuất tức là không nhìn thấy, mà không thấy thì không truy cập được)
Cách xác định toàn cục/biến nội:
Biến toàn cục có thể được khai báo ở bất cứ module nào (trừ class module). Lúc khai báo, cần nhớ rõ rằng biến toàn cục phải được khai báo trước bất cứ lệnh Sub hay Function nào trong module.
Biến nội được khai báo sau lệnh Sub hay Function. Tất cả các biến nội khai báo giữa 2 dòng Sub/Function và EndSub/Function hoàn toàn là của riêng Sub/Func ấy.
Bộ nhớ (life):
VBA chia bộ nhớ ra làm hai phần, phần bộ nhớ ụ (heap memory) và phần bộ nhớ ngăn xếp (stack memory). Tạm thời ở bài này không giải thích tại sao có những tên quái lạ này.
Phần bộ nhớ ụ là phần lâu dài (permanent), thuộc về chung cả Project. Biến chứa trong phần này sẽ giữ trị của chúng cho đến khi bị dọn rác, hoặc cả project kết thúc (hết chạy). Biến toàn cục được VBA chứa trong phần này, vì vậy chúng được khởi trị chỉ một lần (lúc khai báo). Sau đó, chúng tùy thuộc vào chỗ nào hoặc lúc nào gán trị khác.
Phần bộ nhớ ngăn xếp thuộc về function/sub. Biến chứa trong phần này sẽ bị hủy khi function/sub kết thúc (Exit/End Sub/Function). Biến nội được chứa trong phần này, vì vậy chúng được khởi trị với mỗi lượt gọi của sub/func, và xóa trị khi thoát sub/func.
Nói cách khác, biến toàn cục được giữ suôt khoảng thời gian chạy prooject. Biến nội chỉ được giữ trong thời gian chạy sub/func
Khả năng "được nhìn thấy" (visibility):
Biến toàn cục hiển hiện khắp Project. Muốn giấu nó không cho module khác thấy thì khai nó là Private. Từ khóa Private không hề ảnh hưởng tính chất "toàn cục" của biến. Private chỉ là một lệnh dẫn trình dịch, bảo trình dịch rằng "các món này chỉ thấy được trong module này, các modules khác sẽ không nhìn thấy". Nói cách khác, nó vẫn nằm chình ình đó, nhưng module khác truy cập vào sẽ bị ẩy ra (lỗi).
Biến nội thì không cần nói thêm. Của ai nấy xài, sub/func khác không được rớ vào (thực ra thì có thấy được đâu mà "rớ"). Lưu ý rằng dối với sub/func đệ quy thì mỗi lượt gọi tính là một sub/func khác.
Nếu một biến được truy cập đến trong một sub/func mà không được khai báo thì VBA sẽ lần lượt xét:
- Xét tất cả modules để tìm xem biến ấy có được khai báo là toàn cục hay không. Lưu ý rằng nếu không phải module hiện tại thì nó phải được khai Public, VBA mới cho nhìn thấy.
- Nếu không "nhìn thấy" biến toàn cục nào cả thì VBA mặc định đây là biến nội.
Đó là lý do chính mà người ta khuyên dùng Option Explicit. (Không hẳn là việc "tường minh", tránh gõ sai chính tả...)
Nếu tôi đang đọc code mà thấy một biến a được truy cập, không thấy khai báo thì tôi nhớ lại đầu module có thấy câu "Option Excplcit"? Nếu có thì tôi biết chắc a có được khai báo đâu đó, và là toàn cục. Nếu không có thì tôi phải đi tìm xem a có được khai báo Public ở đâu chăng trước khi kết luận nó là toàn cục hay biến nội.
Khi một biến được khai báo nội thì bên trong sub/func ấy, nó sẽ che biến toàn cục trùng tên. Nói cách khá, nếu code của bạn có mọt biến toàn cục a, nhưng bên trong sub xx bạn lại khai báo a (biến nội) thì mội truy cập đến a trong sub xx sẽ là a biến nội, khong phải a toàn cục.
Xong phần lý thuyết, thực hành thì rất dễ:
Vấn đề của bạn thì hiển nhiên là nên khai cái đít sần kia là toàn cục, đỡ phải dựng đối tượng và nạp dữ liệu mỗi lần truy cập.
Cấu trúc thường dùng là
Public DIC As Object
-------------------------- (VBA tự động vẽ một lằn gạch phân biệt giữa khai báo biến toàn cục và sub/func)
Sub KhoiDIC()
Set DIC = CreateObject.....
' đọc data và nạp vào DIC ở đây
End Sub
Sub LamViecA() ' cùng module
If DIC Is Nothing Then KhoiDIC
' làm việc ở đây
End Sub
Sub LamViecB() ' module khác
If DIC Is Nothing Then KhoiDIC
' làm việc ở đây
End Sub
Với cấu trúc code này, DIC là một biến toàn cục, được sử dụng triệt để, mọi sub/func trong module hiện tại hay modules khác đều có thể truy cập và update nó. Vì bạn muốn nhiều modules thấy nó cho nên dùng từ khóa Public. Bình thường tôi sẽ dùng từ khóa Private để tránh rắc rối bằng cách che không cbo các modules khác truy cập nó.
Trong bất cứ code nào cần đến cái Dic này, bạn thêm dòng code xét xem nó đã được nạp dữ liệu chưa, nếu chưa thì dựng đối tượng và tùy tiện nạp dữ liệu. Lưu ý là nếu dữ liêu có thể thay đổi thì code LamViec cũng phải cập nhật nó.
Đại khái là vậy. Bạn làm quen rồi sẽ biết cách sửa đổi cho hạp ý mình, uyển chuyển theo từng hoàn cảnh.