web-dev-qa-db-ja.com

VBA動的配列の移植

次のコードは、エラー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
43

forループでは、次のように配列でRedimを使用します。

For i = 0 to 3
  ReDim Preserve test(i)
  test(i) = 3 + i
Next i
57
Fluffi1974

はい、あなたはReDimステートメントを探しています。これは、配列に必要なスペースを動的に割り当てます。

次の声明

Dim MyArray()

次元のない配列を宣言するため、コンパイラはその大きさを知らず、その中に何も格納できません。

ただし、ReDimステートメントを使用して配列のサイズを変更できます。

ReDim MyArray(0 To 3)

また、preservingのコンテンツの間に配列のサイズを変更する必要がある場合は、PreserveキーワードとともにReDimステートメントを使用できます。

ReDim Preserve MyArray(0 To 3)

ただし、ReDimと特にReDim Preserveの両方のパフォーマンスコストが高いことに注意してください。ループ内でこれを繰り返し行うことは、可能な限り避けてください。ユーザーに感謝します。


ただし、質問に示されている単純な例(単なる使い捨てのサンプルではない場合)では、ReDimはまったく必要ありません。明示的な次元で配列を宣言するだけです:

Dim MyArray(0 To 3)
24
Cody Gray

CodyとBrettが述べたように、Redim Preserveを適切に使用することでVBAのスローダウンを減らすことができます。 Brettは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

これは、サイズが2倍になったときにのみReDim Preserveを呼び出します。メンバー変数eSizeは、eElemsの実際のデータサイズを追跡します。このアプローチは、実行時まで最終的な配列の長さがわからない場合のパフォーマンスの向上に役立ちました。

これが他の人にも役立つことを願っています。

23
a505999

Codyの便利なコメントに加えて、配列の大きさがわからない場合があることに注意してください。この状況での2つのオプションは次のとおりです。

  1. あなたがそれに投げられると思うものを扱うのに十分な大きさの配列を作成する
  2. 感知可能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
11
brettdj

上記の多くの(すべての)投稿が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を使用し、LBound(arr1)/UBound(arr1)呼び出しの直後にErr.Numberをチェックすることです。配列が初期化されている場合は0、そうでない場合は0でなければなりません。 VBAには組み込みの不正な動作があるため、アレイの制限をさらに確認する必要があります。詳細な説明は、誰でも Chip PearsonのWebサイトMankind Treasure Of VBA Wisdom ...)

へー、それは私の最初の投稿で、読みやすいと信じています。

10
Petr Pivonka