Подтвердить что ты не робот

VBA: Различия в двух способах объявления нового объекта? (Попытка понять, почему мое решение работает)

Я создавал новый объект в цикле и добавлял этот объект в коллекцию; но когда я читал назад коллекцию, она всегда была полностью заполнена последним объектом, который я добавил. Я придумал два способа обойти это, но я просто не понимаю, почему моя первоначальная реализация была неправильной.

Оригинал:

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

Это привело к тому, что каждая строка была равна 10; Я, очевидно, не создавал новые объекты, но вместо этого использовал один и тот же цикл через цикл, несмотря на то, что объявление было внутри цикла.

Итак, я добавил Set oMatch = Nothing непосредственно перед строкой Next, и это устранило проблему, теперь она была от 0 до 10. Так что если старый объект был явно уничтожен, то он был готов создать новый? Я бы подумал, что следующая итерация в цикле заставит все, что объявлено в цикле, уничтожить из-за объема?

Любопытно, я попробовал другой способ объявления нового объекта: Dim oMatch As clsMatch: Set oMatch = New clsMatch. Это также приводит к 0-10.

Может кто-нибудь объяснить мне, почему первая реализация была неправильной?

4b9b3361

Ответ 1

Ответ Fink дает вам основную проблему, потому что ваш первый цикл добавляет несколько ссылок на один и тот же экземпляр "clsMatch" в вашу коллекцию. Я просто расскажу о том, почему ваше исправление работает.

В VBA строка типа:

Dim c As New Collection

фактически не создает новую коллекцию. Операция "Dim" всегда является просто декларацией. Подумайте о форме "Как новый" как сокращение для этого:

Dim c As Collection
'...

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

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

'...

Вот почему уничтожение вашей ссылки путем установки переменной, которая содержала ее в "Nothing", работала. [ПРИМЕЧАНИЕ: для тех, кто отредактировал это, чтобы сказать "не было" - это изменяет смысл ответа и делает его неправильным. Пожалуйста, прочтите оригинальный вопрос. OP обнаружил, что установка переменной в Nothing работает, и я объяснял, почему это было так.] Когда цикл вернулся к строке "oMatch.setLineNumber", VBA "полезно" создал новый экземпляр "clsMatch", для вашей переменной "oMatch", к которой нужно обратиться, а затем вы получили несколько разных экземпляров в своей коллекции.

Возможно, было бы лучше сделать это явно:

Dim oMatch As clsMatch   

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

Обратите внимание, что (в отличие от C/С++ или?. NET) не имеет значения, где идет объявление "Dim". Он не "выполняется" несколько раз внутри цикла, и область его объявлений является процедурной, хотя она появляется внутри цикла.

Ответ 2

Когда вы добавляете объект oMatch в коллекцию, передается переменная By Reference. Когда вы снова объявляете oMatch как новый clsMatch, он не уничтожает первый указатель локальной памяти объектов, который вы создали. Это просто дает вам то же локальное расположение памяти, что и первый объект oMatch, который вы создали, даже если вы объявили его как новый объект. VBA использует ByRef как метод передачи памяти по умолчанию. Затем ячейки памяти коллекции обновляются, указывая на одну и ту же ячейку памяти, с обновленным номером строки. Таким образом, все указатели памяти коллекции будут указывать на тот же последний объект, который вы создали.

Когда вы устанавливаете oMatch = ничего, он сбрасывает указатель локальной памяти и создает новый объект oMatch с новым указателем локальной памяти, а указатели на сборку будут указывать на их правильные объекты.

Передача памяти по умолчанию VBA - ByRef, как и VB, где по умолчанию используется ByVal, поэтому вы можете периодически переходить к этому предостережению.

Ответ 3

Существует допустимое использование для "как нового" в модулях класса. Рассмотрим это:

модуль 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

класс myClassX:

Public subClass as new referencedClass ' automatic instanciation of subclass:

class referencedClass:

Public someThing as string