Экземпляры класса VBA

#arrays #excel #vba

#vba #класс #excel #объект

Вопрос:

У меня возникла проблема в VBA, когда каждый элемент в массиве заменяется каждый раз, когда я добавляю что-то в этот массив.

Я пытаюсь просмотреть строки в заданном диапазоне и преобразовать каждую строку этого класса в пользовательский класс (с именем ‘CustomRow’ в приведенном ниже примере). существует также класс manager (называемый ‘CustomRow_Manager’ ниже), который содержит массив строк и имеет функцию для добавления новых строк.

Когда добавляется первая строка, она работает нормально: https://drive.google.com/file/d/0B6b_N7sDgjmvTmx4NDN3cmtYeGs/view?usp=sharing

однако, когда он переходит ко второй строке, он заменяет содержимое первой строки, а также добавляет вторую запись: https://drive.google.com/file/d/0B6b_N7sDgjmvNXNLM3FCNUR0VHc/view?usp=sharing

Есть идеи о том, как это можно решить?

Я создал немного кода, который показывает проблему, посмотрите переменную ‘rowArray’ в классе ‘CustomRow_Manager’

Файл макроса
https://drive.google.com/file/d/0B6b_N7sDgjmvUXYwNG5YdkoySHc/view?usp=sharing

в противном случае код приведен ниже:

Данные

     A   B   C
1   X1  X2  X3
2   xx11    xx12    xx13
3   xx21    xx22    xx23
4   xx31    xx32    xx33
 

Модуль «Module1»

 Public Sub Start()
Dim cusRng As Range, row As Range
Set cusRng = Range("A1:C4")
Dim manager As New CustomRow_Manager
Dim index As Integer
index = 0
For Each row In cusRng.Rows
    Dim cusR As New CustomRow
    Call cusR.SetData(row, index)
    Call manager.AddRow(cusR)
    index = index   1
Next row
End Sub
 

Модуль класса «CustomRow»

 Dim iOne As String
Dim itwo As String
Dim ithree As String
Dim irowNum As Integer


Public Property Get One() As String
    One = iOne
End Property
Public Property Let One(Value As String)
    iOne = Value
End Property

Public Property Get Two() As String
    Two = itwo
End Property
Public Property Let Two(Value As String)
    itwo = Value
End Property

Public Property Get Three() As String
    Three = ithree
End Property
Public Property Let Three(Value As String)
    ithree = Value
End Property

Public Property Get RowNum() As Integer
    RowNum = irowNum
End Property
Public Property Let RowNum(Value As Integer)
    irowNum = Value
End Property

Public Function SetData(row As Range, i As Integer)
    One = row.Cells(1, 1).Text
    Two = row.Cells(1, 2).Text
    Three = row.Cells(1, 3).Text
    RowNum = i
End Function
 

Модуль класса «CustomRow_Manager»

     Dim rowArray(4) As New CustomRow
    Dim totalRow As Integer

    Public Function AddRow(r As CustomRow)
        Set rowArray(totalRow) = r

        If totalRow > 1 Then
            MsgBox rowArray(totalRow).One amp; rowArray(totalRow - 1).One
        End If
        totalRow = totalRow   1
    End Function
 

Комментарии:

1. Прочитайте, например, эту статью о auto-instancing variable . В общем, вам следует избегать автоматического создания экземпляров переменных. HTH

Ответ №1:

Ваша проблема заключается в использовании

 Dim cusR As New CustomRow
 

внутри For цикла. Эта строка фактически выполняется только один раз (обратите внимание, что когда вы выполняете один шаг F8 по коду, он не останавливается на этой строке)

Каждая итерация For цикла использует один и тот же экземпляр cusR. Поэтому все экземпляры manager , добавленные в класс, указывают на одно и то же cusR

Замените это

 For Each row In cusRng.Rows
    Dim cusR As New CustomRow
 

с помощью этого

 Dim cusR As CustomRow
For Each row In cusRng.Rows
    Set cusR = New CustomRow
 

Это явно создает экземпляр нового экземпляра класса

Комментарии:

1. Это сделало свое дело! я даже не думал об этом, поскольку обычно я пишу в .NET, где вы могли бы явно создавать новые экземпляры в цикле.

2. Я чувствую, что это конкретное поведение недостаточно хорошо документировано. Существует разница между Dim v As New ClassObject И Dim v As ClassObject Set v = New ClassObject , однако это единственное место, где я нашел это задокументированным.