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

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

Оригинал:

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.

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

Ответы [ 3 ]

33 голосов
/ 20 марта 2010

Ответ Финка правильно решает вашу главную проблему: ваш первый цикл добавляет несколько ссылок на один и тот же экземпляр 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 сработала , и я объяснил почему так и было.] Когда цикл вернулся к строке '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 / C ++ или ??. NET) не имеет значения, куда идет объявление 'Dim'. Он не «выполняется» несколько раз внутри цикла, и область его действия распространяется на всю процедуру, даже если она появляется внутри цикла.

9 голосов
/ 19 марта 2010

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

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

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

3 голосов
/ 02 февраля 2015

В модулях класса допустимо использование слова «как новый». Учтите это:

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

класс, на который ссылается класс Public someThing as string

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...