VBA Lý Thuyết: Tầm vực và giá trị của biến

Liên hệ QC

VetMini

Ăn cùng góc phố
Tham gia
21/12/12
Bài viết
16,928
Được thích
23,278
Tôi mở thớt này để giải thích bài #1 trong thớt này
15 năm chưa thấy câu trả lời thích đáng.

Bài $1 đố tại sao theo code này:
Private Sub CommandButton1_Click()
K = K + 1
Range("A1").Value = K
End Sub

Biến K không thay đổi, cứ luôn luôn là #1.

Các bài kế tiếp, kể từ bài #2 chỉ mách cho biết cách "khắc phục", tức là làm cho biến K thay đổi chứ không giải thích.

Đây là giải thích, lưu ý là người đọc cần một trình độ tối thiểu về lý thuyết bộ nhớ.

Và cũng cần một ít kiến thức về tầm vực, và mức độ hiển thị (được nhìn thấy) của biến và sub/function.

Ở bài này, tôi nói về mức độ hiển thị trước, bởi vì có thấu hiểu từ này mới dễ hiểu biến toàn cụ, biến nội ... (tấu xảo, nó liên quan đến câu hỏi thứ hai)

VBA chia một Project ra nhiều Modules với mục đích gói gọn và ly khai, giới hạn tầm vực của các món bên trong từng Module.
Người code VBA chia các món này thành hai loại:
1. Loại chỉ dùng trong Module nó khai báo. Các Module khác không được động đến.
2. Loại dùng chung, chia sẻ với mọi Modulkes khác
Loại 1 được khai báo với từ khóa Private. Loại 2 được khai báo với từ khóa Public (VBA mặc định)
Khi một biến hoặc Sub/Function được khai báo dạng 1, nó chỉ được Module này thấy thôi. Code ở các Modules khác sẽ không thấy nó.
Module1:
Private x ' x là biến toàn cục, nhưng chỉ hiện hữu trong Module1
Sub m1()
x = 5 ' x ở đây là x được khai báo ở trên
End Sub

Module2:
Sub m2()
x = 10 ' x ở đây là của Module2, không liên quan gì đến x bên Module1
' nếu Module2 có "Option Explicit" thì trình dịch sẽ báo lỗi là x chưa khai báo.
End Sub


Khi một biến hoặc Sub/Function được khai báo dạng 2, nó được mọi Modules nhìn thấy. Code ở các Modules khác tha hồ sử dụng nó.
Module3:
Public x ' x là biến toàn cục, nhưng chỉ hiện hữu trong Module1
Sub m3()
x = 5 ' x ở đây là x được khai báo ở trên
End Sub

Module4:
Sub m4()
x = 10 ' x ở đây là của Module1
' nếu Module4 có "Option Explicit" thì trình dịch cũng không báo lỗi, vì nó tìm được x ở Module3
End Sub

Vì vậy, cái gì khai là Private trong môt Module là tác giả muốn nó chỉ nhìn thấy được bên trong Module. Cái gì khai Public là tác giả muốn nó dùng tùm lum, ở đâu cũng được.

Lưu ý về từ khóa "che" (hide):
Nếu ở Module4 trên, tác giả khai báo biến x thì biến x này sẽ che biến x khai báo bên trong Module3. Muốn sử dụng x của Module3, code trong Module4 phải ghép tiền tố Module3, tức là Module3.x

Buồn ngủ quá. Để mai sẽ tiếp trả lời câu #1.

23/01/2023 01:00 pm Bổ sung và đính chính:
Đêm qua buồn ngủ quá cho nên lúc copy/paste quên chỉnh nchoox nnayf
Module3:
Public x '
x là biến toàn cục, nhưng chỉ hiện hữu trong Module1
Sub m3()
x = 5 ' x ở đây là x được khai báo ở trên
End Sub

Xin đọc là:
Biến x khai báo thế này là biến toàn cục, hiện hữu trong Module3 và có thể nhìn thấy, truy cập được trong tất cả các Modules khác.
 
Lần chỉnh sửa cuối:
Tiếp tục:
Sỡ dĩ tôi rầy rà nói chuyện Public/Private trước khi vào giải thích câu hỏi #1 là vì vấn đề "hiện huwux" và "quyền truy cập" này có liên quan đến việc trình dịch (VBA compiler) kết nối biến.

Tóm tắt về khái niện toàn cục/nội:

1. VBA khuyến khích lập trình theo kiểu cấu trúc và mô-đun/gói (structured & modular). Để dễ quản lý code, nguồi viết có thể chia từng loại code ra nhiều Modules.

2. Mỗi Module là một khối riêng biệt. Các Modules có thể liên lạc với nhau qua phần giao diện của chúng.

3. Giao diện của một Module là phần được khai báo bằng từ khóa Public. Biến/hằng/phương thức/hàm đều vậy.

4. Mặt khác, phần khai báo với từ khóa Private là của riêng Module, người viết cố ý không muốn cho các Modules khác truy cập.

5. VBA kết nối theo tính thần "gần nhất". Khi hai biến trùng tên thì biến nội sẽ che biến toàn cục. (biến nội là biến khai báo bên trong sub/function, biến toàn cục là biến khai báo bên trong Module nhưng bên ngaoif Sub/Function). Khi hai phần tử toàn cục (biến/hằng/sub/function) trùng tên nhau thì cái bên trong Module hiện tại sẽ che cái ở Module khác.

6. Để tránh VBA lọng cọng với việc trùng tên, người ta có thể sử dụng không gian định danh (namespace), kỹ thutaaj này thêm tiền tố (như tên Module) vào phần tử (biến/hằng/sub/function)
Điển hình, nếu trong Sheet1, tôi sửa sub bắt sự kiện nào đó thành Public
Public Sub Worksheet_SelectionChange(ByVal Target As Range)
MsgBox "here"
End Sub
Thì ở Module khác (Module1) tôi có thể gọi thẳng sub này mà không cần tạo sự kiện
Sub t()
Sheet1.Worksheet_SelectionChange Sheet1.Range("a1")
End Sub
Chú ý: vì mỗi sheet đều có một Worksheet_SelectionChange riêng của chúng cho nên tôi phải dùng tiền tố Sheet1 để xác định với trình dịch rằng tôi muôn kết nối với cái ở sheet1 chứ không phải sheet2,...
 
Đến phần kết nối.
Kết nối là công việc mà trình dịch VBA thực hiện để nối tên biến với ngăn chứa trong bộ nhớ.
Ví dụ biến x là Long, được trình dịch nối với ngăn chứa ở địa chỉ ABCD (Hex). Vì x là Long cho nên ngăn chứa này gồm tất cả 4 bytes.
x = 1
Khi chạy đến lệnh này, VBA sẽ tìm xem x được chứa ở nơi nào và nhét trị số 1 vào 4 bytes ấy => 0001
Kết nối có hai dạng là kết nối sớm, khi trình dịch dịch thấy biến và kết nối trễ, khi code VBA chạy. Để giản dị một chút, tôi sẽ không nói tới loại kết nối trễ

Ví dụ trong Module1, tôi có sub Sub1()
Khi trình dịch VBA bắt đầu đọc đọc code Module1, nó sẽ lượt ra tất cả các biến được khai báo trên đầu, trước các sub/function, các biến này là biến toàn cục.
Khi trình dịch VBA đọc code sub Sub1, nó sẽ lượt ra tất cả các dòng khai báo Dim (và Static), và tùy theo kiểu biến, nó phân phát chỗ trong bộ nhớ (dĩ nhiên các kiểu phức tạp như mảng động thì không thể phân phát bộ nhớ cho nên nó tạm thời để đó - gần như kết nối trễ)
Ben trong m1, tôi có dòng lệnh
x = 1
Khi đọc đến lệnh này, trình dịch sẽ thực hiện các bước tuần tự sau:
1. Duyệt lại bảng tên biến xem x có được sub Sub1 khai báo bằng từ khóa Dim hay Static?
2. Nếu có thì x là biến nội
3. Nếu không có thì trình dịch tiếp tục duyệt xem x có được khai báo ở đầu Module1? Nếu có thì x là biến toàn cục.
4. Nếu không có thì trình dịch sẽ tiếp tục lục tìm xem các Modules khác có cái nào khai báo x là biến Public? Nếu có thì x cũng là biến toàn cục, hiện hữu trogn Module kia và có thể truy cập được ở mọi nơi trong Project.
5. Nếu vẫn không có thì x được trình dịch mặc định là biến nội của Sub1.
Đến đây thì cũng nên biết lệnh dẫn Option Explicit đóng vai trò gì?
Explicit tiếng Anh có nghĩa là "minh bạch, huỵch toẹt, không hiểu ngầm, không mặc định"
Lệnh dẫn này buộc trình dịch không được sử dụng bước thứ 5. Nếu đến bước 4 mà chưa thỏa thì báo lỗi trình dịch.

Tưởng cũng cần nhắc là quan niệm ở GPE này dùng lệnh dẫn Option Explicit để buôc người viết code phải "khai báo biến tường minh" hầu tránh truonwgf hợp gõ sai chính tả tên biến, 1 biến thành 2...
Nhưng quan niệm của tôi nó lý thuyết hơn. Mục đích chính của lệnh dẫn này là để ta đọc code. Khi tôi đọc code thấy một biến không được khai báo trong Sub/Function thì tôi tự động biết nó là biến toàn cục. Nếu không thấy trên đầu Module này thì cũng ở Module nào đó. Nếu không có lệnh dẫn này thì tôi sẽ không biết nó là loại biến gì, phải đi tìm xem nó có khai báo ở đâu.

Hết phần kết nối. Ngày mai sẽ tiếp về bộ nhớ và giải thích đúng mục tiêu.
 
Tóm lược về biến và tầm vực của biến:
* Có hai loại biến, biến nội và biến toàn cục.

1. Biến toàn cục được khai báo bên trong bất cứ một Module nào, nhưng bên ngoài các block code Sub và Function.
(a) - Biến toàn cục được khai báo với từ khóa Public hoặc Private.
(b) - Nếu khai báo với từ khóa Public, biến này có thể nhìn thấy được, và do vậy có thể truy cập được trong tất cả các code thuộc về Project
(c) - Nếu khai báo với từ khóa Private, biến chỉ nhìn thấy được đối với code bên trong Module mà nó khai báo. Và hiển nhiên là biến chỉ có thể truy cậ đươc trong các code thuộc về Module.
(d) - Biến toàn cục có đời sống trải dài suốt chương trình. Chỉ khi ta tắt Project (Application) hoặc gặp lệnh End (Lưu ý: lệnh End chấm dứt chương trình, cần phân biệt với End Sub/Function là chấm dứt khối code) thì nó mới hết.

2. Biến nội được khai báo bên trong cụm code Sub hoặc Function.
(a) - Biến nội được khai báo với từ khóa Dim hoặc Static.
(b) - Biến nội hoàn toàn thuộc về Sub/Function đã khai báo nó. Bên ngoài không thể nhìn thấy, không thể truy cập.
(c) - Biến nội khai báo với từ khóa Dim là nội thật sự. Tức là nó thuộc hẳn về cái lần ọi (instance) Sub/Function ấy. Nếu Sub/Function được gọi nhiều lần, mỗi lần gọi sẽ sử dụng một bộ biến nội khác. Kể cả khi gọi đệ quy (recursive), mỗi lượt gọi là một bộ biến khác.
(d) - Biến nội khai báo với từ khóa Dim có đời sống đi kèm với Khối code Sub/Function. Khi thoát với lệnh Exit Sub/Function hay End Sub/Function thiif nó mới bị hủy. Ngay cả lúc code gọi hàm khác thì chúng chỉ tạm ngưng hoạt động; sau khi trở về từ hàm được gọi thì chúng hiện hữu trở lại. (xem giải thích ở phần bộ nhớ/vùng nhớ)
(e) - Biến nội khai báo với từ khóa Static có đời sống trải dài suốt chương trình (giống như biến toàn cục). Ngược với biến Dim, biến Static là biến chung của tất cả mọi lần gọi Sub/Function.

Về bộ nhớ/vùng nhớ:
Trong lúc chướng trình chạy, bộ nhớ được nền tảng (VBA) chia làm hai phần, phần Ụ (heap memory) và phần ngăn xếp (stack memory)

4. Heap memory là phần nhớ chung. Các biến chứa trong phần này sẽ có đời sống suốt chương trình và không bị VBA hủy.
VBA chứa biến toàn cục và biến Static trong phần này.
Đúng với tên Ụ (heap) nó được cấp phát cho biến một cách loạn xạ, tùy thích của trình dịch (kết nối sớm) và VBA (kết nối trễ)

5. Stack memory là phần nhớ được VBA giành cho biến nội Đúng với tên ngăn xếp (stack), nó được sử dụng theo luật LIFO (last in first out).
VBA chứa biến nội trong phần này.
Cứ mỗi lân Sub/Function được gọi, các biến nội của Sub/Function lần lượt được dựng lên (lưu ý kỹ từ "dựng" này, có nghĩa là biến được khởi tạo lại) và đưa vào ngăn xếp (tiếng nhà nghề của ngăn xếp gọi là push). Khi thoát khỏi Sub/Function để trở về nơi được gọi, các biến này được lấy ra (tiếng nhà nghề là pop) và hủy. Vì vậy, biến nội chỉ có đời sống trong vòng lượt chạy hiện tại của Sub/Function.

Hết phần lý thuyết

Chú thích (chơi cho vui, không quan trọng): Public và Private nghe còn có vẻ hữu lý. Nhưng Dim và Static là cái quái gì? Chả thấy liên quan gì đến biến cả.
Thực sự chúng có liên quan đến lịch sử ngôn ngữ lập trình và IBM. Hôm nào rảnh tôi sẽ kể cho quý vị nghe.
Từ Dim nếu nguyên vẹn thì có nghĩa là "mờ", nhưng trong ngữ cảnh của ngôn ngữ lập trình thì nó là viết tắt của Dimension (chiều, của array)
Từ Static có nghĩa là "tĩnh", trái vơi Dynamic có nghĩa là "động".
 
Hết bài học rồi. Bây giờ đến phần bài tập.
Câu hỏi bài #1 trả lời thế nào?

Private Sub CommandButton1_Click()
K = K + 1
Range("A1").Value = K
End Sub

Code này không cho biết một số chi tiết bên ngoài nó. Cho nên ở một số chỗ bắt buộc phải đoán.

Chiếu theo lý thuyết về kết nối ở bài #3, và ta giả sử rằng bài toán này không có Module nào khác. Cách dò tính chất của biến K sẽ lần lượt đi qya bước 1, 2, 3, 4 và dừng lại ở bước 5: trình dịch VBA mặc định K là biến nội.

Chiếu theo lý thuyết về tầm vực và đời sống của biến ở bài #4, mục 5; K là biến nội cho nên được chứa trong phần nhớ ngăn xếp (stack) và có đời sống trong vòng lượt chạy của Sub CommandButton1_Click. Chạy xong, K bị popped ra khỏi stack và hủy. Lượt kế tiếp, K được dựng lại từ đầu (bài #4, mục 5, câu 3). Lưu ý rằng VBA mặc định trị ban đầu lúc mới dựng của biến kiểu số là 0.

Vì vậy, theo code trên, sau khi chạy sub lần thứ nhất thì trị 1 được gán vào ô A1. Và mõi lượt chạy kế tiếp vẫn được gán trị 1.

Câu hỏi có đặt rằng:
Tôi muốn khi nhấn vào 1 button thì cell A1 sẽ tăng lên 1 đơn vị...
Và K hiển nhiên đã không thỏa đáng nguyện vọng người viết code.
Bài #2 của thớt ấy trả lời không chính xác lắm.
Anh muốn biến K tăng thì Anh cần khai báo biến Public
Thực tế, chỉ cần khai bên ngoài Sub là nó đã là biến toàn cục, Public hay Private đều được (bài #4 ở trên, mục 1, câu đầu tiên). Public chỉ cần thiết khi khai báo nó trong một Module khác.
Cách thứ hai của bài giải kia là một thuật toán khác hoàn toàn. Thuật toán này vượt ngoài đời sông và tầm vực của biến, sử dụng cái mà lập trình gọi là "persistent values". Ở đây, tác giả dùng một đối tượng có tính chất "dai dẳng" hơn để chứa trị. Đối tượng này là Range của WorkSheet. Dùng thủ thuật persistent values thì phải biết rõ trị ấy có cần được lưu giữ sau khi chương trình chấm dứt? Nếu không thì phải có code đưa Range ấy về trị ban đầu.
 
Bài tập 2:
Câu 2 của đề bài #1 "Đố vui về VBA" trên cũng đã có câu trả lời.
Tuy nhiên, các câu trả lời chỉ thỏa mãn trên môi trường GPE (hầu hết dân ở đây có phong cách viết code gióng nhau), và điều kiện curaGPE (một người viết code). Nếu qua môi trường khác (không phải GPE) hay điều kiện khác (nhiều người viết) thì các giải thích ấy cồn thiếu sót.
Tôi đem ra làm bài tập ở đây cho các bạn cần phải học cách làm việc "không GPE"

Trong suốt bào này, tôi sẽ gọi phuonwg thức và hàm Sub/Function bằng một cái tên chung là hàm con cho gọn.

CÂU HỎI 2: Sub và Private Sub?
Sau khi hoàn tất câu hõi 1, giờ tôi sữa code 1 chút, đặt code vào 1 module rồi dùng Command Button đễ gọi Sub thông qua lệnh Call
Sub Add()
Cho code vào đây!
End Sub
Private Sub CommandButton1_Click()
Call Add
End Sub

Hoàn toàn ko có vấn đề gì về cách viết này.. và các bạn cũng thấy các cao thủ đã từng viết kiểu như vậy!
Cho hỏi: Cách viết trong câu hỏi 1 là gọi trực tiếp code bằng Button, còn cách viết thứ 2 này thì gọi code thông qua lệnh Call... Vậy có gì khác nhau giữa 2 cách viết và tại sao đôi khi ta lại cần làm như vậy? Sao ko cho code trực tiếp vào mà lại thông qua Call Add chi cho mất công thế?


Câu hỏi 2 là câu đầu gà đít vịt. Đầu bài thì hỏi về cách tách code thành hàm con. Cuối bài thì nói chuyện Private/Public. Vùi vậy, bài tập này cũng gồm 2 phần.

Phần 1 - Tách code ra thành hàm con riêng

Câu trả lời chung ở thớt kia là để dễ quản lý và để dùng trong trường hợp code cần chạy nhiều lần, hoặc cần được gọi từ nhiều nơi.

Nếu các bạn thông suốt lý thuyết về tầm vực biến của chủ đề này thì sẽ biết rằng lời giải đáp kia còn thiếu mục đích thứ ba của hàm con.

Hàm con có 3 mục đích:

1. Thu gọn các code được lặp lại nhiều lần. Thay vì phải copy/paste hoặc gõ lại đoạn code và chỉnh sửa một vài thông số thì người ta đặt code ấy vào hàm con, lúc cần thì gọi, hàm và các thông số cần sửa đổi thì đưa qua các tham số khi gội hàm. Các giải đáp trước đây có đề cập đến điều này.

2. Quản lý code, các hàm con sẽ giúp "chia để trị". Công việc nào ra công việc nấy. Mõi công việc là một hàm con.
Các giải đáp trước đây cũng có đề cập đến một phần của ddieuf này. Tuy nhien, ở GPE này người ta không chú tâm đến việc ấy lắm. Tôi đã thấy nhiều trường hợp một hàm lớn làm một đóng công việc khác nhau (tùy theo tham số nạp vào). Theo đúng nguyên tắc lập trình cấu trúc thì đáng lẽ mooix công việc oohair được cho vào một hàm con riêng. Hàm lớn chỉ chiếu theo điều kiện mà gọi hàm con thích nghi.

3. Bảo vệ biến của code gọi hàm. Điều này khá lạ đối với dân GPE. Nhưng nếu bạn thử viết một hàm với vài chục biến xem. Một lúc loạn xạ lên sẽ khó quản lý được biến nào đang làm gì và chứa cái gì. Cách dễ nhất là tách đoạn code riêng ra. Théo lý thuyết tầm vực ở bài này, các biến nội của hàm mẹ sẽ được bảo vệ, hàm con không chạm vào được. Nếu hàm mẹ muốn đưa biến cho hàm cobn thì dùng tham số, cho phép dùng và sử trị thì nạp theo ByRef, chỉ cho phép xem trị nhwngb không cho sửa thì dùng ByVal.

Sao không cho code trực tiếp vào mà lại thông qua Call Add chi cho mất công thế?
Điều 1 và 2 trên trả lời câu hỏi theo khía cạnh quản lý code - và đó cũng là cách trả lời chung trong thớt kia.
Điều 3 trên trả lời câu hỏi theo khía cạnh làm việc chung với nhiều người. Mình không biết những người kia sẽ sử lý code hàm mẹ thế nào.
Đặt ở ham con riêng, toi chỉ phải lo sao cho code thực hiện đúng nhiệm vụ của nó. Viết thẳng code chen vào giũa một đám code đã có sẵn, tôi phải xem lại nó có đưa một số biến nào đó vào tình trạng ngoài ý muốn?

Phần 2 - Private và Public.

Chính người đặt câu hỏi đã giải thích bằng ví dụ toa lét riêng/công cộng. Tuy ví dụ này không sai nhưng nếu đứng trên quan điểm gói gọn của xu hướng Lập Trình Hướng Đối Tượng thì câu trả lời này vắn tắt quá.

Xín nhắc lại bài #2 ở trên, Module là một phần phân ra của Project. Mỗi Module có quyền chứa biến/hằng (coi như thuộc tính) và hàm con (coi như phương thức). Cái gì nó đưa ra Public là cái nó muốn trưng cho công chúng (các Modules khác) xài - gọi là giao diện của Module. Cái gì nó đặt Private là nó không muốn ai biết tới.

Tưởng tượng Module1 nọ (không phải mọi Modules) là một miếng đất. Ở mặt tiền, chủ đất muốn kinh doanh nên xây một khách sạn. Phía sau, chủ đất xây thêm cái nhà riêng để mình và gia đình cư ngụ.

Khu nhà riêng, chủ muốn chứa ai thì chứa, muốn trồng cây, hoa quả gì thì trồng.

Khu khách sạn, đương nhiên nếu chủ không cho ai vào thì còn làm ăn quái gì nữa.
Vì vậy, chẳng những chủ phải quéo-cờm thiên hạ (code ở các Module khác) mà còn phải trưng bày rõ rệt số phòng ốc, tiện nghi, giá cả, cách đăng ký, thanh toán... (các kiểu biến, các hàm con, nhiệm vụ hàm, và cách gọi hàm)., Những cái trưng bày này không bắt buộc nhưng làm ăn muốn khấm khá thì phải biết quảng bá. Người được mướn làm phải biết diễn tả cho khách hàng tương lai hàng của mình, điều lệ sử dụng, ...
Ví dụ như khách sạn có phòng hôi họp và dịch vụ cung ứng hội nghị thì phải cho biết chi tiết, phòng bao nhiêu ghế, dịch vụ cung cấp thức uống hay cả thức ăn, vân vân...

Những chi tiết khác như nhân viên phục vụ là người nhà hay mướn nơi khác (gọi hàm con của Module khác), thức ăn rau cải mua ngoài chợ hay cây nhà lá vườn thì có thể là chi tiết riêng tư. Sẽ đặt nằm trong phần Private vì khách hàng không cần biết tới.

Đối với chủ đất, điểm quan trọng nhất khi phân biệt Public và Private là:

4. Public là giao diện, là những gì mà chủ hứa là sẽ có và nếu cần thì sẽ cung ứng. Vì vậy khi quảng bá thì phải cẩn thận, lời hứa rất khó thay đổi. Thêm thì còn tương đối nhưng rút bớt hay thay các điều lệ thì không nên. Thường thì nếu phải thay đổi, người ta ra phiên bản khác.
Ví dụ hôm nay khoe dịch vụ ăn uống không có bột ngọt. Ngày mai có người gọi phở, vác bát đi mua, tiệm thảy một nắm bột ngọt vào là không được. Khách hàng lên cơ tim, đưa ra tòa cho vỡ nợ.
Hôm nay cho biết hàm ABC trả về một số Double, ngày mai sửa lại thành Long thì hỏng bét.

5. Private là chỗ làm việc phía sau, chủ không hề hứa hẹn cái gì trong Private với khách hàng.
Người viết code cứ việc thay đổi sửa chữa cái gì tùy ý.
Nếu tôi không hề quảng cáo bên Public rằng tôi dùng rau sạch thì việc tôi dùng cây nhà lá vườn hay mua rau chợ là việc của tôi. Khách hàng không cần biết, mà có biết thì cũng chả có gì chắc chắn, bởi vì hôm nay tôi có thể hái rau nhà nhưng ngày mai vườn hết rau, tôi lại ra chợ mua.
Hôm nay biến def tôi khai báo là Double, nhưng ngày mai tôi tính lại Long hiệu quả hơn thì tôi cứ việc đổi. Miễn là mọi việc vẫn êm xuôi. Những lập trình viên khác không can dự đến viết code cho Module này (chỉ viết code Modules khác, truy cập phần giao diện) không ai cần biết và cũng không ai có thể biết rằng biến def có sự thay đổi.
 
Web KT
Back
Top Bottom