2012-01-13 25 views
35

Il seguente codice mi dà errore 9 "indice fuori intervallo". Intendevo dichiarare una matrice dinamica in modo che la dimensione cambi mentre aggiungo elementi ad essa. Devo creare uno "spot" sull'array prima di memorizzare qualcosa come in JS?Compilazione di array dinamici VBA

Sub test_array() 
    Dim test() As Integer 
    Dim i As Integer 
    For i = 0 To 3 
     test(i) = 3 + i 
    Next i 
End Sub 

risposta

47

nel ciclo for usare un Redim sulla matrice come qui:

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

Perché dovresti farlo * in * il ciclo? 'ReDim', e specialmente quando aggiungi' Preserve', è un potenziale killer delle prestazioni. Sapete quante volte il ciclo andrà a scorrere, quindi fatelo sicuramente al di fuori del ciclo. Quindi stai ridimensionando l'array una sola volta e non hai bisogno di 'Preserve'. –

+9

@CodyGray Hai perfettamente ragione se la dimensione finale dell'array è già definita all'entrata del ciclo. Mettere i Redim all'interno del ciclo sarebbe un killer delle prestazioni. Tuttavia, ho presupposto che la dimensione della matrice non sia determinata entrando nel ciclo. Altrimenti l'intero campione non ha alcun senso ... – Fluffi1974

+3

L'asterisco * deve * essere definito al momento di entrare nel ciclo. Devi definire l'intervallo del ciclo. Anche se è una variabile piuttosto che una costante come '3', stai ancora specificando un limite superiore nell'istruzione' For'. Da usare per inizializzare dinamicamente la dimensione dell'array. –

21

Sì, si sta cercando il ReDim dichiarazione, che alloca dinamicamente la quantità di spazio nella matrice.

la seguente dichiarazione

Dim MyArray() 

dichiara un array senza dimensioni, in modo che il compilatore non sa quanto è grande e non è possibile memorizzare qualsiasi cosa all'interno di esso.

Ma è possibile utilizzare l'istruzione ReDim per ridimensionare la matrice:

ReDim MyArray(0 To 3) 

E se è necessario ridimensionare la matrice, mentre conservando il suo contenuto, è possibile utilizzare la parola chiave Preserve insieme con l'affermazione ReDim:

ReDim Preserve MyArray(0 To 3) 

Ma notare che sia ReDim e in particolare ReDim Preserve hanno un costo delle prestazioni pesante. Cerca di evitare di fare questo più e più volte in un ciclo se possibile; i tuoi utenti ti ringrazieranno.


Tuttavia, nel semplice esempio mostrato nella tua domanda (se non è solo un campione usa e getta), non è necessario ReDim affatto. Basta dichiarare la matrice con le dimensioni esplicite:

Dim MyArray(0 To 3) 
+3

+1 anche se aggiungerei che il limite inferiore può e secondo me deve essere specificato esplicitamente: 'ReDim MyArray (0 A 3)' –

+1

@Jean: Questo è un buon consiglio. Molte persone sono morse dal fatto che VB (A) supporta limiti inferiori diversi da 0. Essere espliciti è sempre una buona pratica. –

9

Oltre alle osservazioni utili di Cody vale la pena notare che a volte non si sa quanto è grande il vostro allineamento dovrebbe essere. Le due opzioni in questa situazione sono

  1. Creazione di un array grande abbastanza per gestire qualsiasi cosa si pensa saranno gettati a esso
  2. Sensible uso di Redim Preserve

Il codice di seguito fornisce un esempio di una routine che avrà una dimensione pari a myArray in linea con la variabile lngSize, quindi aggiungerà ulteriori elementi (pari alla dimensione iniziale dell'array) utilizzando un test Mod ogni volta che il limite superiore sta per essere superato

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 
20

Primo poster volta, lettore di molto tempo. Come menzionato da Cody e Brett, è possibile ridurre il rallentamento VBA con un uso ragionevole di Redim Preserve. Brett ha suggerito Mod per farlo.

Per fare ciò è anche possibile utilizzare un utente definito Type e Sub.Prendere in considerazione il mio codice qui sotto:

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 

Ciò richiede ReDim Preserve solo quando la dimensione è raddoppiato. La variabile membro eSize tiene traccia delle dimensioni effettive dei dati di eElems. Questo approccio mi ha aiutato a migliorare le prestazioni quando la lunghezza finale dell'array non è nota fino al runtime.

Spero che questo aiuti anche gli altri.

+1

ti rendi conto che questa domanda è stata posta (e risposta) 18 mesi fa, giusto? –

+15

sì! volevo offrire un'alternativa anche agli altri lettori. Mi rendo conto che l'op ha già accettato la risposta precedente. Grazie. – a505999

+0

Bene, allora ecco un +1 per te, interessante alternativa! :) –

8

Vedo molti (tutti) i messaggi di cui sopra basandosi su LBound/UBound invita ancora potenzialmente non inizializzata VBA array dinamico, ciò che provoca la morte inevitabile di applicazione ...

codice irregolare:

Dim x As Long Dim arr1() As SomeType ... x = UBound(arr1) 'crashes

Corretto codice:

Dim x As Long Dim arr1() As SomeType ... ReDim Preserve arr1(0 To 0) ... x = UBound(arr1)

... vale a dire qualsiasi codice in cui Dim arr1() viene seguito immediatamente da LBound(arr1)/UBound(arr1) chiamate senza ReDim arr1(...) in mezzo, si blocca. La rotonda utilizza uno On Error Resume Next e controlla lo Err.Number subito dopo la chiamata LBound(arr1)/UBound(arr1) - dovrebbe essere 0 se l'array è inizializzato, altrimenti diverso da zero. Poiché esiste un comportamento scorretto incorporato di VBA, è necessario verificare ulteriormente i limiti dell'array. Spiegazione dettagliata può leggere a tutti Chip Pearson's website (che dovrebbe essere celebrato come un umanità Treasure of Wisdom VBA ...)

Eh, questo è il mio primo post, credo che sia leggibile.

+0

Quel commento tutto codificato sopra affidandosi a ubound/lbound non è corretto – brettdj

Problemi correlati