VBA: Differenza in due modi di dichiarare un nuovo object? (Cercando di capire perché la mia soluzione funziona)

Stavo creando un nuovo object all’interno di un ciclo e aggiungendo quell’object a una raccolta; ma quando ho riletto la collezione dopo, è stata sempre riempita interamente con l’ultimo object che avevo aggiunto. Ho inventato due modi per aggirare questo problema, ma semplicemente non capisco perché la mia implementazione iniziale sia sbagliata.

Originale:

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 

Ciò ha comportato che ogni numero riga fosse 10; Ovviamente non stavo creando nuovi oggetti, ma utilizzando lo stesso ogni volta attraverso il ciclo, nonostante la dichiarazione fosse all’interno del ciclo.

Quindi, ho aggiunto Set oMatch = Nothing immediatamente prima della riga Next , e questo ha risolto il problema, era ora da 0 a 10. Quindi se il vecchio object era stato esplicitamente distrutto, allora era disposto a crearne uno nuovo? Avrei pensato che la successiva iterazione attraverso il ciclo avrebbe causato la distruzione di qualsiasi cosa dichiarata all’interno del loop a causa dell’oscilloscopio?

Curioso, ho provato un altro modo per dichiarare un nuovo object: Dim oMatch As clsMatch: Set oMatch = New clsMatch . Anche questo porta a 0 a 10.

Qualcuno può spiegarmi perché la prima implementazione è stata sbagliata?

La risposta di Fink risolve il problema principale, ovvero che il tuo primo ciclo aggiunge più riferimenti alla stessa istanza di “clsMatch” alla tua raccolta. Ti spiegherò perché la tua correzione funziona.

In VBA, una riga come:

 Dim c As New Collection 

in realtà non crea una nuova collezione. L’affermazione ‘Dim’ è sempre solo una dichiarazione. Pensa che la forma “Come nuovo” è una scorciatoia per questo:

 Dim c As Collection '... '(later, when you're about to use 'c') If c Is Nothing Then Set c = New Collection End If '... 

Questo è il motivo per cui distruggere il tuo riferimento impostando la variabile che lo conteneva su “Nothing” stava funzionando. [NOTA: a chi ha modificato questo messaggio per dire “non è stato” – questo cambia il significato della risposta e la rende scorretta. Si prega di leggere la domanda originale. L’OP ha riscontrato che l’impostazione della variabile su Nothing non funzionava e stavo spiegando il perché .] Quando il loop tornava alla riga ‘oMatch.setLineNumber’, VBA “utilmente” ha creato una nuova istanza di ‘clsMatch’ per la tua variabile ‘oMatch’ a cui fare riferimento, e quindi hai più istanze diverse nella tua collezione.

Probabilmente sarebbe meglio farlo esplicitamente:

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

Nota che (a differenza di C / C ++ o ??. NET) non importa dove vada la dichiarazione “Dim”. Non viene “eseguito” più volte all’interno del ciclo e l’ambito di ciò che dichiara è a livello di procedura anche se appare all’interno del ciclo.

Quando aggiungi l’object oMatch alla raccolta, passa la variabile By Memory Reference. Quando stai dichiarando oMatch di nuovo come un nuovo clsMatch, non sta distruggendo i primi oggetti puntatore di memoria locale che hai creato. Ti dà semplicemente la stessa posizione di memoria locale del primo object oMatch che hai creato anche se lo hai dichiarato come nuovo object. VBA utilizza ByRef come tecnica di passaggio della memoria predefinita. Le posizioni della memoria di raccolta vengono quindi aggiornate, puntando entrambe alla stessa posizione di memoria, con il numero di riga appena aggiornato. Quindi tutti i puntatori della memoria di raccolta puntano allo stesso ultimo object che hai creato.

Quando imposti oMatch = nothing, reimposta il puntatore di memoria locale e creerà un nuovo object oMatch con un nuovo puntatore di memoria locale, ei puntatori della raccolta punteranno tutti agli oggetti corretti.

Il passaggio predefinito della memoria di VBA è ByRef, come indicato in VB in cui il valore predefinito è ByVal, quindi potresti incappare in questo avvertimento ogni tanto.

C’è un uso valido per “come nuovo” all’interno dei moduli di class. Considera questo:

modulo a:

 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 

class myClassX:

 Public subClass as new referencedClass ' automatic instanciation of subclass: 

class referencedClass:

 Public someThing as string