Следующий код дает мне ошибку 9 "индекс вне диапазона". Я хотел объявить динамический массив, чтобы размер изменялся по мере добавления к нему элементов. Должен ли я создавать "пятно" в массиве, прежде чем я что-то храню в нем, как в JS?
Sub test_array()
Dim test() As Integer
Dim i As Integer
For i = 0 To 3
test(i) = 3 + i
Next i
End Sub
в вашем цикле for используйте Redim для массива, как здесь:
For i = 0 to 3
ReDim Preserve test(i)
test(i) = 3 + i
Next i
Да, вы ищете оператор ReDim
, который динамически выделяет требуемое количество пространства в массиве.
Следующее утверждение
Dim MyArray()
объявляет массив без измерений, поэтому компилятор не знает, насколько он велик и не может хранить что-либо внутри него.
Но вы можете использовать оператор ReDim
для изменения размера массива:
ReDim MyArray(0 To 3)
И если вам нужно изменить размер массива при сохранении его содержимого, вы можете использовать ключевое слово Preserve
и инструкцию ReDim
:
ReDim Preserve MyArray(0 To 3)
Но обратите внимание, что как ReDim
, так и особенно ReDim Preserve
имеют высокую производительность. Старайтесь избегать этого снова и снова в петле, если это вообще возможно; ваши пользователи будут вам благодарны.
Однако в простом примере, показанном в вашем вопросе (если это не просто выборка), вам вообще не нужно ReDim
. Просто объявите массив с явными размерами:
Dim MyArray(0 To 3)
ReDim MyArray(0 To 3)
Первый плакат, долгое время читатель. Как упомянуто Коди и Бреттом, вы можете уменьшить замедление VBA с разумным использованием Redim Preserve
. Бретт предложил Mod
сделать это.
Для этого также можно использовать пользовательские Type
и Sub
. Рассмотрим мой код ниже:
Public Type dsIntArrayType
eElems() As Integer
eSize As Integer
End Type
Public Sub PushBackIntArray( _
ByRef dsIntArray As dsIntArrayType, _
ByVal intValue As Integer)
With dsIntArray
If UBound(.eElems) < (.eSize + 1) Then
ReDim Preserve .eElems(.eSize * 2 + 1)
End If
.eSize = .eSize + 1
.eElems(.eSize) = intValue
End With
End Sub
Это вызывает Redim Preserve
только при удвоении размера. Переменная-член eSize
отслеживает фактический размер данных eElems
. Этот подход помог мне улучшить производительность, когда конечная длина массива неизвестна до времени выполнения.
Надеюсь, что это тоже поможет другим.
В дополнение к полезным комментариям Коди стоит отметить, что время от времени вы не будете знать, насколько велик ваш массив. В этой ситуации два варианта:
Redim Preserve
Ниже приведен пример процедуры, которая будет измерять myArray
в соответствии с переменной lngSize
, а затем добавить дополнительные элементы (равные размеру начального массива) с помощью теста Mod
всякий раз, когда верхняя граница будет превышена
Option Base 1
Sub ArraySample()
Dim myArray() As String
Dim lngCnt As Long
Dim lngSize As Long
lngSize = 10
ReDim myArray(1 To lngSize)
For lngCnt = 1 To lngSize*5
If lngCnt Mod lngSize = 0 Then ReDim Preserve myArray(1 To UBound(myArray) + lngSize)
myArray(lngCnt) = "I am record number " & lngCnt
Next
End Sub
Я вижу много (всех) сообщений выше, полагающихся на LBound
/UBound
, называет еще потенциально неинициализированный динамический массив VBA, что вызывает неизбежную смерть приложения...
Ошибочный код:
Dim x As Long
Dim arr1() As SomeType
...
x = UBound(arr1) 'crashes
Правильный код:
Dim x As Long
Dim arr1() As SomeType
...
ReDim Preserve arr1(0 To 0)
...
x = UBound(arr1)
... т.е. любой код, в котором Dim arr1()
следует немедленно с помощью LBound(arr1)
/UBound(arr1)
вызовов без ReDim arr1(...)
между ними, происходит сбой. Круговое движение должно использовать On Error Resume Next
и проверить Err.Number
сразу после вызова LBound(arr1)
/UBound(arr1)
- оно должно быть 0, если массив инициализирован, иначе отличный от нуля. Поскольку существует некоторое ошибочное поведение VBA, необходима дальнейшая проверка пределов массива. Подробное объяснение может быть прочитано на веб-сайте Чипа Пирсона (который следует отметить как сокровище человечества мудрости VBA...)
Хех, это мой первый пост, считаю, что это разборчиво.
ReDim
, особенно когда вы добавляетеPreserve
, является потенциальнымReDim
производительности. Вы знаете, сколько раз цикл будет повторяться, поэтому определенно сделайте это вне цикла. Тогда вы изменяете массив только один раз, и вам не нуженPreserve
.