2010-03-19 4 views
27

Estaba creando un nuevo objeto dentro de un ciclo y agregando ese objeto a una colección; pero cuando volví a leer la colección, siempre estaba completa con el último objeto que había agregado. He encontrado dos formas de evitar esto, pero simplemente no entiendo por qué mi implementación inicial fue incorrecta.VBA: ¿Diferencia en dos formas de declarar un objeto nuevo? (Tratando de entender por qué mi solución funciona)

original:

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 

Esto dio lugar a todos los lineNumber siendo 10; Obviamente no estaba creando nuevos objetos, sino que usaba el mismo cada vez que pasaba el ciclo, a pesar de que la declaración estaba dentro del ciclo.

Entonces, agregué Set oMatch = Nothing inmediatamente antes de la línea Next, y esto solucionó el problema, ahora era 0 a 10. Entonces, si el antiguo objeto se destruía explícitamente, ¿estaba dispuesto a crear uno nuevo? Pensé que la próxima iteración a través del ciclo causaría que algo declarado dentro del ciclo fuera destruido debido al alcance?

Curioso, probé otra manera de declarar un nuevo objeto: Dim oMatch As clsMatch: Set oMatch = New clsMatch. Esto también da como resultado de 0 a 10.

¿Alguien me puede explicar por qué la primera implementación fue incorrecta?

+1

En VBA es considéré d mala práctica para usar "Como nuevo". Como descubriste por las malas, puede conducir a todo tipo de errores sutiles. Lo mejor es declarar todas las variables en la parte superior y usar "= Nuevo" cuando corresponda. (Nota al margen: diría exactamente lo contrario en VB.Net) –

+1

El alcance más pequeño posible en VBA es el nivel de procedimiento; un bloque 'For ... Next' (o cualquier otro bloque) no crea un alcance. –

Respuesta

28

La respuesta de Fink hace que su principal problema sea correcto, que es que su primer ciclo agrega varias referencias a la misma instancia de 'clsMatch' a su colección. Voy a explicar en detalle por qué funciona tu solución.

En VBA, una línea como:

Dim c As New Collection 

en realidad no crear una nueva colección. La declaración 'Dim' es siempre solo una declaración. Piense en el 'como nuevo' forma como taquigrafía para esto:

Dim c As Collection 
'... 

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

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

'... 

Esa es la razón por la destrucción de su referencia estableciendo la variable de que la contenía a 'Nada' estaba trabajando. [NOTA: a quien editó esto para decir "no era" - eso cambia el significado de la respuesta y la hace incorrecta. Por favor lea la pregunta original. El OP encontró que el ajuste de la variable a Nothing funcionaba, y estaba explicando por qué ese fue el caso.] Cuando el ciclo regresó al 'oMatch.setLineNumber 'línea, VBA "de manera útil" creó una nueva instancia de' clsMatch 'para su variable' oMatch 'a la que hacer referencia, y luego obtuvo múltiples instancias diferentes en su colección.

Probablemente sería mejor hacer esto de forma explícita:

Dim oMatch As clsMatch 

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

Tenga en cuenta que (a diferencia de C/C++ o ?? NET.) No importa donde la declaración 'Dim' va. No se está "ejecutando" varias veces dentro del ciclo, y el alcance de lo que declara es todo el procedimiento a pesar de que aparece dentro del ciclo.

+0

Gracias por explicar que el alcance no es como C/C++, eso es a lo que estoy más acostumbrado, en cuanto al comportamiento esperado (aprendida primero en C, así que supongo que todos los idiomas actuarán de manera similar). – Matt

+4

Es por eso que a menudo verá (corrija, IMO) un consejo como el comentario de Jonathan Allen a su pregunta de que 'Como nuevo' generalmente es mejor evitarlo. 'Dim Como ' declara cosas. 'Establecer = Nuevo ' crea cosas. Pero 'Dim como nuevo ' es un híbrido extraño. Combine el hecho de que la forma 'Dim As New' parece que está creando una instancia en ese momento con el hecho de que VBA le permitirá ponerlo en un bucle y la confusión es comprensible. – jtolle

6

Cuando agrega el objeto oMatch a la colección, pasa la variable Por referencia de memoria. Cuando vuelve a declarar oMatch como una nueva clsMatch, no destruye el primer puntero de memoria local de objetos que había creado. Simplemente le da la misma ubicación de memoria local que el primer objeto oMatch que creó aunque lo haya declarado como un objeto nuevo. VBA utiliza ByRef como la técnica predeterminada para pasar la memoria. Las ubicaciones de memoria de recopilación se actualizan, ambas apuntando a la misma ubicación de memoria, con el número de línea recientemente actualizado. Por lo tanto, todos los punteros de memoria de recopilación van a apuntar al mismo último objeto que ha creado.

Cuando configura oMatch = nada, restablece el puntero de la memoria local, y crea un nuevo objeto oMatch con un nuevo puntero de memoria local, y los punteros de la colección apuntarán a sus objetos correctos.

El pase de memoria predeterminado de VBA es ByRef, ya que está en VB, donde el valor predeterminado es ByVal, por lo que podría encontrarse con esta advertencia de vez en cuando.

1

Existe un uso válido para "como nuevo" dentro de los módulos de clase. Considere esto:

módulo

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 

clase myClassX:

Public subClass as new referencedClass ' automatic instanciation of subclass: 

referencedClass clase:

Public someThing as string 
Cuestiones relacionadas