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

VBA - Возвращаемый массив из свойства Get

Если массивы возвращаются по ссылке, почему не работает следующее:

'Class1 class module
Private v() As Double
Public Property Get Vec() As Double()
    Vec = v()
End Property
Private Sub Class_Initialize()
    ReDim v(0 To 3)
End Sub
' end class module

Sub Test1()
    Dim c As Class1
    Set c = New Class1
    Debug.Print c.Vec()(1) ' prints 0 as expected
    c.Vec()(1) = 5.6
    Debug.Print c.Vec()(1) ' still prints 0
End Sub
4b9b3361

Ответ 1

В VBA массивы никогда не возвращаются по ссылке, если они не возвращаются через параметр ByRef. Кроме того, всякий раз, когда вы используете = для назначения массива переменной, вы сделали новую копию массива, даже если вы назначили его аргументу ByRef внутри процедуры, так что вы в значительной степени удачи, пытаясь сделать эту работу.

Некоторые альтернативы...

  • Используйте VBA.Collection вместо массива.
  • Создайте свой собственный класс, который инкапсулирует массив и предоставляет процедуры для косвенного доступа и управления внутренним массивом.

Ответ 2

У вас нет свойства let. Кроме того, свойство get возвращает весь массив, а не только этот элемент. Измените возвращаемый тип свойства Get из Double() на простое двойное. Добавить свойство Let. Обратите внимание, что он принимает два входа, но ему передается только одно. Предполагается, что последняя переменная (MyValue, в данном случае) получает значение от значения после знака =. Поместите точку разрыва где-нибудь в начале теста Test1() и посмотрите, как это значение влияет на окно Locals. Сравните переменные, созданные исходным кодом по сравнению с моим кодом:

'Class1 class module
Private v() As Double
Public Property Get Vec(index As Long) As Double
    Vec = v(index)
End Property
Public Property Let Vec(index As Long, MyValue As Double)
    v(index) = MyValue
End Property
Private Sub Class_Initialize()
    ReDim v(0 To 3)
End Sub
' end class module

'Begin module
Sub Test1()
    Dim c As Class1
    Set c = New Class1
    Debug.Print c.Vec(1) ' prints 0 as expected
    c.Vec(1) = 5.6
    Debug.Print c.Vec(1) ' prints 5.6
End Sub
'End module  

Ответ 3

Я хочу предложить другой хороший способ сделать это, используя Collection и static Property без необходимости использовать класс:

представьте, что вы хотите иметь перечисление xlCVError в виде массива (или коллекции), например, чтобы проходить по нему в случае ошибок и обрабатывать его на основе фактической ошибки.

Следующее инициализируется один раз при доступе:

'from https://stackoverflow.com/a/56646199/1915920
Static Property Get XlCVErrorColl() As Collection
    Dim c As Collection  'will be already initalized after 1st access
                         'because of "Static Property" above!
    Set XlCVErrorColl = c
    If Not c Is Nothing Then Exit Property

   'initialize once:

    Set c = New Collection
    c.Add XlCVError.xlErrDiv0
    c.Add XlCVError.xlErrNA
    c.Add XlCVError.xlErrName
    c.Add XlCVError.xlErrNull
    c.Add XlCVError.xlErrNum
    c.Add XlCVError.xlErrRef
    c.Add XlCVError.xlErrValue
    Set XlCVErrorColl = c
End Property

Превратить это в массив или реализовать его в виде массива просто, но коллекции кажутся мне более полезными, с недостатком в том, что их элементы не являются неявно типизированными /(compile-time-) тип проверен.
Так что это, например, превратит его в массив (только для чтения) (с недостатком in-mem-copy-недостатка, упомянутым в других ответах/комментариях):

'from https://stackoverflow.com/a/56646199/1915920
Static Property Get XlCVErrorArr() As XlCVError()
   Dim a() As XlCVError
   XlCVErrorArr = a
   If UBound( a ) > 0 Then Exit Property

   'initialize once:

   Dim c As Collection:  Set c = XlCVErrorColl
   ReDim a(c.Count)
   Dim i As Integer:  For i = 1 To c.Count 
       a(i) = c(i)
   Next i
   XlCVErrorArr = a
End Function

Таким образом, преобразуя пример из ответа Clayton Ss в статическое, модифицируемое свойство модуля, используя некоторый массив, это будет:

'module (no class required)
'from https://stackoverflow.com/a/56646199/1915920

Private v() As Double

Static Property Get Vec(index As Long) As Double
    If UBound(v) < 3 Then  'initialize once:
      ReDim v(0 To 3)  'one could initialize it with anyting after here too
    end if

    Vec = v(index)
End Property

Public Property Let Vec(index As Long, MyValue As Double)
    v(index) = MyValue
End Property

Ответ 4

Vec (индекс As Long, MyValue As Double)

Что здесь делает ключевое слово index? Пожалуйста, направьте меня на веб-страницу, где я могу узнать об индексе, связанном с этим массивом.