2015-03-07 4 views
1

Я пишу VBA макрос в Excel 2013. У меня есть код ниже, чтобы прочитать ряд в вариант,Excel VBA - Дальность считывания в Variant, сохраняя при этом индекс совпадает с номерами столбцов

Dim MyBuffer As Variant 

With MyWorkSheet 
    MyBuffer = .Range(.Cells(1, NAME_COL), .Cells(10, AGE_COL)).Value 
End With 

'Here NAME_COL = 5, AGE_COL = 9 

Теперь MyBuffer является 2-мерный массив с индексами от (1, 1) до (10, 5). Мне было интересно, есть ли способ сохранить вторую часть индексов, такую ​​же, как и номера столбцов. т.е. от (1, 5) до (10, 9), так что я могу использовать константы NAME_COL, AGE_COL и т. д., также обращаясь к варианту. Это прежде всего для удобства чтения (так что другой программист может легко увидеть, к какой записи я обращаюсь) и ремонтопригодности (случаи, когда мы добавляем/удаляем/свопим столбцы на листе). Цените любую помощь при вычислении того же самого. Пожалуйста, обратите внимание, что я не рассматриваю возможность дальнейшего копирования в другой массив с модифицированным индексом или с другим набором констант для позиций Variant (NAME_POS, AGE_POS и т. Д.).

ответ

1

Просто переустановите его.

With MyWorkSheet 
    MyBuffer = .Range(.Cells(1, NAME_COL), .Cells(10, AGE_COL)).Value 
    ReDim Preserve MyBuffer(LBound(MyBuffer) To UBound(MyBuffer), NAME_COL To AGE_COL) 
End With 
+0

Благодаря GSerg. Когда я смотрю на ReDim, я понимаю, что он создает новый массив за кулисами и копирует в него данные (или это не будет в вышеупомянутом случае, так как нет изменения размера?). У меня есть несколько таких буферов и гораздо больший объем данных (следовательно, производительность), которые нужно учитывать (т.е. почему я избегал явного копирования в другой массив). – mpathi

+1

@mpathi Насколько я вижу, это непосредственно [манипулирует] (http://stackoverflow.com/a/11713643/11683) дескриптор SAFEARRAY, просто изменяя границы измерений. Вы можете проверить это, посмотрев на VarPtr (MyBuffer (LBound (MyBuffer, 1), LBound (MyBuffer, 2))) - он не перемещается. Также он работает только потому, что «MyBuffer» объявлен как один «вариант». Если вы объявите его как массив вариантов, он не будет удален, потому что в этом случае он попытается скопировать и для этого новые границы должны должным образом перекрывать старые границы, которых они не в этом случае (хотя это я любопытное размышление здесь). – GSerg

+0

Обратите внимание, что это то, что я наблюдал, я не знал этого из теории. Вы должны действительно измерить время, необходимое для восстановления огромного массива, посмотрите, занимает ли он незаметно небольшое количество времени, независимо от размера массива. – GSerg

0

Основываясь на предложении GSerg, я выполнил следующий тест для измерения производительности, а также изменений в адресе памяти.

// Shift column indices, without increasing number of columns 
1000R x 20C  --> 1000R x 20C  = 4.7 micro secs (address remains same) 
100,000R x 20C --> 100,000R x 20C = 6.3 micro secs (address remains same) 

// Shift column indices, and increase number of columns 
1000R x 20C  --> 1000R x 21C  = 80 micro secs (address changes) 
100,000R x 20C --> 100,000R x 21C = 13.5 milli secs (address changes) 

Ниже приводится код, в случае, если кто интересуется

Option Explicit 

Declare Function GetFrequency Lib "kernel32" Alias "QueryPerformanceFrequency" (Frequency As Currency) As Long 
Declare Function GetTickCount Lib "kernel32" Alias "QueryPerformanceCounter" (TickCount As Currency) As Long 

Sub Test() 
    Dim MyWorkSheet As Worksheet 
    Dim MyBuffer As Variant 

    Dim FirstRow As Long 
    Dim FirstCol As Long 
    Dim LastRow As Long 
    Dim LastCol As Long 

    Dim Message As String 

    Set MyWorkSheet = Sheets("Test") 
    FirstRow = 1 
    FirstCol = 1 
    LastRow = 100000 
    LastCol = 20 

    ' Read the range into buffer 
    With MyWorkSheet 
     MyBuffer = .Range(.Cells(FirstRow, FirstCol), .Cells(LastRow, LastCol)).Value 
    End With 

    ' Check the address before ReDim 
    Message = "Value At " & VarPtr(MyBuffer(FirstRow, FirstCol)) & " = " & MyBuffer(FirstRow, FirstCol) 

    ' Shift the column indices 
    FirstCol = FirstCol + 100 
    LastCol = LastCol + 100 ' Modify this to change column count as well 

    ' ReDim the buffer to shifted column indices and measure time taken 
    Timer 
    ReDim Preserve MyBuffer(FirstRow To LastRow, FirstCol To LastCol) 
    Timer 

    ' Check the address after ReDim 
    Message = Message & Chr(10) & "Value At " & VarPtr(MyBuffer(FirstRow, FirstCol)) & " = " & MyBuffer(FirstRow, FirstCol) 

    MsgBox Message 
End Sub 

Sub Timer() 
    Dim TickCount As Currency 
    GetTickCount TickCount 

    Static Frequency As Currency 
    If Frequency = 0 Then 
     GetFrequency Frequency 
    End If 

    Static FirstTime As Double 

    If Frequency Then 
     If FirstTime <> 0 Then 
      MsgBox "Elapsed : " & (TickCount/Frequency) - FirstTime 
      FirstTime = 0 
     Else 
      FirstTime = TickCount/Frequency 
     End If 
    End If 
End Sub