Khi khai báo Object trong VBA, việc sử dụng New có gì dở?

vbano1

SMod
Thành viên BQT
Khi khai báo Object, chúng ta có hai cách khai báo phổ biến như sau:
Phương pháp 1: Sử dụng New
Mã:
Dim C As New Collection
Phưong pháp 2: Sử dụng Set
Mã:
Dim C As Collection
Set C = New Collection
Thông thường mọi người thích sử dụng cách 1 vì gõ code nhanh hơn.
Nhất là những người sử dụng máy tính xách tay, kích cỡ màn hình bé, nên viết code tiết kiệm được 1 dòng thì thích lắm.

Ta biết rằng, một số đối tượng, nó không lập tức được tạo ra. Khi ta sử dụng nó, nó sẽ được kiểm tra là đã tồn tại hay chưa, rồi mới được tạo ra. Việc này cũng có ưu điểm và nhược điểm.
Để đảm bảo đối tượng bị hủy (không cho tồn tại), tôi sẽ thiết định nó là Nothing. Hãy xem code dưới đây.
Mã:
Sub test1()
    Dim C As New Collection
    Set C = Nothing
    C.Add "Tai sao thiet dinh la Nothing ma van Add duoc?"
    MsgBox C.Item(1)
End Sub
Kết quả:
Bạn cần đăng nhập để thấy đính kèm

Cái gì thế này, rõ ràng tôi đã thiết định là Nothing, tại sao nó vẫn hoạt động nhỉ?
Nào, hãy cùng tôi thực hiện ví dụ tiếp theo.
Mã:
Sub loisexayra()
    Dim C As Collection
    Set C = New Collection
    Set C = Nothing
    C.Add "Khong the add duoc"
    MsgBox C.Item(1)
End Sub
Lỗi 91 xảy ra, chương trình không chạy được. Bạn hãy tự kiểm chứng.

Như vậy, bằng thực nghiệm ta thấy rằng, nếu khai báo là New, dù hủy nó đi, sau đó nếu ta tiếp tục sử dụng lại, thì đối tượng này sẽ tự động được tạo ra.
Nhân tiện phải nói rằng, việc sử dụng các biến đối tượng không chỉ gói gọn trong việc sử dụng các thuộc tính hay thực thi phương thức.
Đoạn code dưới đây sẽ cho thấy rằng khi gán biến đối tượng là Nothing, rồi so sánh xem nó có phải là Nothing hay không, kết quả sẽ cho ra là False.
Mã:
Sub KhongTheTroThanhNothing()
    Dim C As New Collection
    
    Set C = Nothing
    Debug.Print C Is Nothing
    
    Set C = Nothing
    Debug.Print C Is Nothing
End Sub
Bạn cần đăng nhập để thấy đính kèm

Còn nữa.
Dịch từ nguồn:
 

Euler

Administrator
Thành viên BQT
Về chi phí
Khi khai báo bằng New, mỗi khi tham chiếu tới đối tượng, sẽ phát sinh chi phí kiểm tra xem đối tượng có phải là Nothing hay không.
Theo MSDN nói rằng chúng ta không nên sử dụng khai báo New vì việc này làm phát sinh chi phí kiểm tra. Sau đây chúng ta sẽ tiến hành thực nghiệm để kiểm tra việc này.
Mã:
Sub Tacbiet()
'Khai báo biến và New tách ra làm hai dòng code'
    t = Timer
    Dim C As Collection
    Set C = New Collection
    For i = 1 To 10000000
        C.Add i
    Next
    Debug.Print Timer - t
End Sub

Sub dongthoi()
'Khai báo biến và New xảy ra đồng thời'
    t = Timer
    Dim C As New Collection
    For i = 1 To 10000000
        C.Add i
    Next
    Debug.Print Timer - t
End Sub
Kết quả là không khác nhau nhiều. Việc tách biệt hai dòng code cho tốc độ nhanh hơn nhưng sự khác biệt này không lớn.
10 triệu lần sự khác biệt ở 0.3~0.4s.
(Còn nữa)
 
Khi khai báo, nếu sử dụng NEW thì thời gian kích hoạt của hàm sẽ thay đổi
Thực ra thời điểm khi Dim C As New Collection được thực thi thì Collection vẫn chưa được nạp vào biến C.
trng thực tế khi Collection được cất vào biến số thì một lần nữa biến Object đó được đem ra sử dụng.
Collection hơi khó hiểu. Để kiểm tra điều này, tôi sẽ tạo Class riêng và kiểm chứng.
Hãy tạo Class1 và dán code sau vào.
Class1:
Private Sub Class_Initialize()
    Debug.Print "Obj phat sinh"
End Sub

Sub hoge()
    Debug.Print "hoge"
End Sub
Tiếp theo tạo Module 1 và viết code sau:
Mã:
Sub phantachkhaibaovaNew()
    Dim c As Class1
    Debug.Print "Code1"
    Set c = New Class1
    Debug.Print "Code2"
    c.hoge
End Sub
Kết quả như sau:
Mã:
Code1
Obj phat sinh
Code2
hoge
Tiếp theo hãy dán code sau vào Module 1 và chạy nó:
Mã:
Sub KhaiBaoNewDongThoi()
    Dim c As New Class1
    Debug.Print "Code1"
    c.hoge
End Sub
Kết quả như sau:
Mã:
Code1
Obj phat sinh
hoge
Bạn thấy đấy, đối tượng không được tạo ra khi khai báo trong New tại thời điểm khai báo. Nếu không thì đã có dòng thông báo Obj phat sinh xuất hiện trước dòng Code1.
Sau đó, thời điểm lệnh được đưa ra với c.hoge, code khởi tạo được thực thi sớm hơn mệnh lệnh này (c.hoge) một chút vì vậy dòng thông báo Obj phat sinh xuất hiện trước dòng Code2.
Tiếp theo, ta thử tham chiếu đến biến đối tượng mà không thực thi phương thức nào.
Tôi đã thử sử dụng hàm VarPtr để tìm ra địa chỉ của một biến.
Hãy dán code sau vào Module 1 và thực thi nó:
Module1:
Sub ChithamchieuNew合()
    Dim c As New Class1
    Debug.Print "Code1"
    Debug.Print VarPtr(c)
    Debug.Print "Code2"
    c.hoge
End Sub
Kết quả:
Mã:
Code1
Obj phat sinh
 2116545165968
Code2
hoge
Ngay khi ta tham chiếu tới địa chỉ biến c thì đối tượng này được sinh ra, minh chứng là hàm khởi tạo đã hoạt động nên đã xuất hiện thông báo Obj phat sinh.
 
Bug xảy ra do khai báo biến Object bằng New
Bug không phải là lỗi mà đó là tất cả những gì hoạt động ngoài ý đồ của người code.

Sau đây ta sẽ tạo Class. Trên đó viết code như sau.
Mã:
Private Sub Class_Initialize()
    Sheet1.Range("A1").Value = "Hello"
End Sub

Sub hoge()
    Sheet1.Range("A1").Value = "hoge"
End Sub

Private Sub Class_Terminate()
    Sheet1.Range("A1").Value = "Good Bye!"
End Sub
Trên Module1 ta viết code:
Mã:
Sub khaibaovaNew()
'Phân tách khai báo và New ra hai dòng code'
    Sheet1.Range("A1").Value = "Start"
    Dim c As Class1
    Set c = New Class1
    Debug.Print Sheet1.Range("A1").Value
    c.hoge
    Debug.Print Sheet1.Range("A1").Value
    Set c = Nothing
    Debug.Print Sheet1.Range("A1").Value
End Sub
Kết quả:
Mã:
Hello
hoge
Good Bye!
Đầu tiên chương trình viết chữ Start vào ô A1.
Khi New Class được gán cho biến c. Vì vậy phương thức khởi tạo (Class_Initialize) được gọi. Ô A1 được ghi nội dung Hello. Vì vậy mà Debug.Print xuất ra ký tự là Hello.

Tiếp theo ta sẽ kiểm nghiệm code sau sử dụng khai báo New :
Mã:
Sub KhaibaoNew()
    Sheet1.Range("A1").Value = "Start"
    Dim c As New Class1
    Debug.Print Sheet1.Range("A1").Value
    c.hoge
    Debug.Print Sheet1.Range("A1").Value
    Set c = Nothing
    Debug.Print Sheet1.Range("A1").Value
End Sub
Kết quả:
Mã:
Start
hoge
Good Bye!
Khi khai báo biến bằng New, biến Obj không được tạo ra, do đó giá trị ô A1 là Start.
Tiếp theo hoge được thực thi, giá trị ô A1 được ghi là Hello nhưng ngay sau đó vì hoge được thực thi nên giá trị ô A1 lại chuyển thành hoge.
Có thể nói đây là một bug vì nó thực hiện một việc làm vô nghĩa. Hoặc trong một tình huống khác, mặc dù bạn đã gán cho biến đối tượng là Nothing nhưng đoạn code liên quan tới đối tượng đó vẫn hoạt động khi tham chiếu tới đối tượng này sau đó.
Vấn đề còn tồi tệ hơn khi biến đối tượng với tư cách là một biến nội bộ, được tái tạo lại và đoạn code như trên (Class_Initialize) sẽ lại được thực thi.
À đây là một bug trong lập trình.

Tuy nhiên nếu bạn tách ra làm hai dòng khai báo, khai báo biến, và sau đó dùng Set đã gán đối tượng.
Thế thì khi bạn hủy biến đối tượng (gán là Nothing), sau đó lại tham chiếu tới biến đối tượng này, thì chắc chắn sẽ có thông báo lỗi xảy ra.
 
Top