Как изменить элемент массива модуля класса, который является элементом словаря?

#arrays #excel #vba #class #dictionary

#массивы #excel #vba #класс #словарь

Вопрос:

Что у меня есть:

  1. Словарь (sData).
  2. Все элементы в sData являются экземплярами модуля класса (cls).
  3. Каждый экземпляр модуля класса содержит 2D-массив (cls.arrAtt).
  4. arrAtt объявлен как общедоступный вариант в модуле класса
  5. Базовые знания VBA

Что я пытаюсь сделать:

Замените все «истинные» и «ложные» элементы массива на «Да» и «Нет».

 For Each key In sData
    Set cls = sData(key)
    For j = LBound(cls.arrAtt, 2) To UBound(cls.arrAtt, 2)
        If cls.arrAtt(1, j) = "true" Then cls.arrAtt(1, j) = "Yes"
        If cls.arrAtt(1, j) = "false" Then cls.arrAtt(1, j) = "No"
    Next j
Next key
  

введите описание изображения здесь

(ЕСЛИ операторы запускаются правильно)

Как вы можете видеть на рисунке, цикл по массиву не изменяет его элементы.

Есть идеи, почему?

Вот как заполняются массивы:

 with sh1
    For i = 2 To .UsedRange.Rows.count
       If sData.Exists(.Cells(i, rCol).Value2) Then
           Set cls = sData(.Cells(i, rCol).Value2)
       Else
           Set cls = New cls_Source_Attributes
           sData.Add .Cells(i, rCol).Value2, cls
       End If
    
       cls.idRow = i
       cls.arrAtt = .Range("" amp; .Cells(i, firstCol).Address amp; ":" amp; .Cells(i, lastCol).Address amp; "")
    Next i
End With
  

Ответ №1:

вы должны добавить «метод» в свой класс, например

 Public Sub arrAttChange(i As Long, j As Long, val As Variant)
    arrAtt(i, j) = val
End Sub 
  

а затем используйте его в своем коде следующим образом:

 For Each key In sData
    Set cls = sData(key)
    For j = LBound(cls.arrAtt, 2) To UBound(cls.arrAtt, 2)
        If cls.arrAtt(1, j) = "true" Then cls.arrAttChange 1, j, "Yes"
        If cls.arrAtt(1, j) = "false" Then cls.arrAttChange 1, j, "No"
    Next
Next key
  

Улучшение этого кода с логической точки зрения может быть

 For Each key In sData
    Set cls = sData(key)
    With cls ' <-- reference the object

        For j = LBound(.arrAtt, 2) To UBound(.arrAtt, 2)
            Select Case .arrAtt(1, j)
                Case "true"
                    .arrAttChange 1, j, "Yes"

                Case "false"
                    .arrAttChange 1, j, "No"
            End Select
        Next

    End With
Next
  

Другой возможностью является использование «вспомогательного» массива в коде «потребителя» класса, оставляя код класса нетронутым

 Dim arrAtt As Variant ' declare a "helper" array
For Each key In sData
    Set cls = sData(key)
    arrAtt = cls.arrAtt ' initialize the helper array with the values from the class one and change it in the following For - Next loop
    For j = LBound(arrAtt, 2) To UBound(arrAtt, 2)
        Select Case arrAtt(1, j)
            Case "true"
                arrAtt(1, j) = "Yes"

            Case "false"
                arrAtt(1, j) = "No"
        End Select
    Next
    cls.arrAtt = arrAtt ' assign the changed array back to the class array
Next
  

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

1. Извините за мой поздний отзыв. Спасибо за ваш быстрый ответ и советы тоже. Не могли бы вы объяснить мне, почему это работает именно так, а не так, как я пытался? И почему cls.arrAtt=arrAtt работает (последняя строка), а cls.arrAtt(1,j)= «No» не работает? Я думал, что это одно и то же.

2. @HYs, я, по общему признанию, не знаю реальной глубокой причины этого, но я предполагаю, что этот Public элемент Variant типа может быть назначен только как «целое», как вы сами это делаете cls.arrAtt = .Range("" amp; .Cells(i, firstCol).Address amp; ":" amp; .Cells(i, lastCol).Address amp; "") . Хотя его можно запросить в его «деталях»