2010-03-19 18 views
27

Tôi đã tạo một đối tượng mới trong vòng lặp và thêm đối tượng đó vào bộ sưu tập; nhưng khi tôi đọc lại bộ sưu tập sau đó, nó luôn được lấp đầy hoàn toàn với vật thể cuối cùng tôi đã thêm vào. Tôi đã đưa ra hai cách xung quanh điều này, nhưng tôi chỉ đơn giản là không hiểu tại sao thực hiện ban đầu của tôi là sai.VBA: Sự khác biệt trong hai cách tuyên bố một đối tượng mới? (Cố gắng hiểu tại sao giải pháp của tôi hoạt động)

gốc:

Dim oItem As Variant 
Dim sOutput As String 
Dim i As Integer 

Dim oCollection As New Collection 
For i = 0 To 10 
    Dim oMatch As New clsMatch 
    oMatch.setLineNumber i 
    oCollection.Add oMatch 
Next 
For Each oItem In oCollection 
    sOutput = sOutput & "[" & oItem.lineNumber & "]" 
Next 
MsgBox sOutput 

Điều này dẫn đến mỗi linenumber là 10; Tôi rõ ràng là không tạo ra các đối tượng mới, nhưng thay vào đó sử dụng cùng một đối tượng mỗi lần thông qua vòng lặp, bất chấp việc khai báo nằm bên trong vòng lặp. Vì vậy, tôi đã thêm Set oMatch = Nothing ngay trước dòng Next và điều này đã khắc phục được sự cố, bây giờ là 0 đến 10. Vì vậy, nếu đối tượng cũ bị phá hủy rõ ràng, thì nó sẵn sàng tạo một cái mới? Tôi đã có thể nghĩ rằng lặp đi lặp lại tiếp theo thông qua vòng lặp sẽ gây ra bất cứ điều gì tuyên bố trong vòng lặp làm bị phá hủy do phạm vi?

Tò mò, tôi đã thử một cách khác để khai báo một đối tượng mới: Dim oMatch As clsMatch: Set oMatch = New clsMatch. Điều này cũng vậy, dẫn đến 0 đến 10.

Bất cứ ai có thể giải thích cho tôi tại sao triển khai đầu tiên không đúng?

+1

Trong VBA là considere d thực hành xấu để sử dụng "Như Mới". Khi bạn phát hiện ra một cách khó khăn, nó có thể dẫn đến tất cả các loại lỗi tinh tế. Tốt nhất là khai báo tất cả các biến của bạn ở trên cùng và sử dụng "= Mới" khi thích hợp. (Lưu ý phụ: Tôi sẽ nói chính xác ngược lại trong VB.Net) –

+1

Phạm vi nhỏ nhất có thể trong VBA là mức độ thủ tục; khối 'Đối với ... Tiếp theo' (hoặc bất kỳ khối nào khác) không tạo phạm vi. –

Trả lời

28

Câu trả lời của Fink là vấn đề chính của bạn, đó là vòng lặp đầu tiên của bạn đang thêm nhiều tham chiếu đến cùng một ví dụ 'clsMatch' vào bộ sưu tập của bạn. Tôi sẽ chỉ giải thích tại sao sửa chữa của bạn hoạt động.

Trong VBA, một dòng như:

Dim c As New Collection 

không thực sự tạo ra một bộ sưu tập mới. Câu lệnh 'Dim' luôn là tuyên bố. Hãy nghĩ đến biểu mẫu 'Như Mới' là viết tắt của điều này:

Dim c As Collection 
'... 

'(later, when you're about to use 'c') 

If c Is Nothing Then 
    Set c = New Collection 
End If 

'... 

Đó là lý do hủy tham chiếu của bạn bằng cách đặt biến chứa thành 'Không có gì' đang hoạt động. [LƯU Ý: để bất cứ ai chỉnh sửa điều này để nói "không phải" - điều đó thay đổi ý nghĩa của câu trả lời và làm cho nó không chính xác. Vui lòng đọc câu hỏi gốc. OP thấy rằng việc đặt biến thành Không có gì đã làm công việc và tôi đã giải thích lý do tại sao trường hợp đó.] Khi vòng lặp quay lại 'oMatch.setLineNumber 'line, VBA "helpfully" đã tạo một cá thể mới của' clsMatch 'cho biến' oMatch 'của bạn để tham chiếu đến, và sau đó bạn có nhiều cá thể khác nhau trong bộ sưu tập của mình.

Nó có lẽ sẽ tốt hơn để làm điều này một cách rõ ràng:

Dim oMatch As clsMatch 

For i = 0 To 10     
    Set oMatch = New clsMatch     
    oMatch.setLineNumber i     
    oCollection.Add oMatch     
Next 

Lưu ý rằng (không giống như trong C/C++ hoặc ?? NET.) Nó không quan trọng nơi khai các 'Dim' đi. Nó không được "thực hiện" nhiều lần bên trong vòng lặp, và phạm vi của những gì nó tuyên bố là toàn bộ thủ tục mặc dù nó xuất hiện bên trong vòng lặp.

+0

Cảm ơn bạn đã giải thích rằng phạm vi không giống như C/C++, đó là những gì tôi được sử dụng nhiều, theo như mong đợi hành vi (học đầu tiên trên C, vì vậy tôi chỉ giả định tất cả các ngôn ngữ sẽ hành động tương tự). – Matt

+4

Đó là lý do tại sao bạn thường sẽ thấy (đúng, IMO) tư vấn như bình luận của Jonathan Allen cho câu hỏi của bạn rằng 'Như Mới' thường là tốt nhất nên tránh. 'Dim ' tuyên bố nội dung. 'Đặt = Mới ' tạo nội dung. Nhưng 'Dim As New ' là một kết hợp lạ. Kết hợp một thực tế là hình thức 'Dim As New' trông giống như nó tạo ra một thể hiện ngay sau đó với thực tế là VBA sẽ cho phép bạn đặt nó trong một vòng lặp và nhầm lẫn là dễ hiểu. – jtolle

6

Khi bạn thêm đối tượng oMatch vào bộ sưu tập, nó sẽ chuyển biến theo Tham chiếu bộ nhớ. Khi bạn đang khai báo oMatch một lần nữa như là một clsMatch mới, nó không phá hủy các đối tượng đầu tiên của bộ nhớ cục bộ mà bạn đã tạo ra. Nó chỉ đơn giản là cung cấp cho bạn vị trí bộ nhớ cục bộ giống như đối tượng oMatch đầu tiên mà bạn đã tạo ra ngay cả khi bạn đã khai báo nó như một đối tượng mới. VBA sử dụng ByRef làm kỹ thuật truyền bộ nhớ mặc định. Các vị trí bộ nhớ bộ sưu tập sau đó được cập nhật, cả hai đều trỏ đến cùng một vị trí bộ nhớ, với số dòng mới được cập nhật. Vì vậy tất cả các bộ nhớ con trỏ bộ sưu tập sẽ trỏ đến cùng một đối tượng cuối cùng mà bạn đã tạo.

Khi bạn đặt oMatch = không có gì, nó sẽ đặt lại con trỏ bộ nhớ cục bộ và sẽ tạo đối tượng oMatch mới với con trỏ bộ nhớ cục bộ mới, và con trỏ của bộ sưu tập sẽ trỏ đến đối tượng chính xác của chúng.

Việc truyền bộ nhớ mặc định của VBA là ByRef, như được gán cho VB ở vị trí mặc định là ByVal, vì vậy bạn có thể chạy vào cảnh báo này mỗi lần một lần nữa.

1

Có sử dụng hợp lệ cho "mới" trong mô-đun lớp học. Xem xét việc này:

mô-đun một:

Dim mUbelow as myClassX  ' do not use "as new" here 
set mUbelow = new myClassX ' mUbelow instanciation also instanciates subClass 
           ' as a referencedClass object 
           ' so you can not forget to do this 
mUbelow.subClass.someThing = "good news" ' without the "as new" below: ==> error 

lớp myClassX:

Public subClass as new referencedClass ' automatic instanciation of subclass: 

lớp referencedClass:

Public someThing as string 
Các vấn đề liên quan