Здравствуйте, недавно начал читать книжку по Excel VBA, и как новичек, не понимал зачем же нужно писать Dim ..., эксель ведь не глупый, разберется. Пока не увидил в книжке отличный пример кода.
Код
Sub TimeTest()
Dim x As Long, y As Long
Dim A As Double, B As Double, C As Double
Dim i As Long, j As Long
Dim StartTime As Date, EndTime As Date
' Sohranenie vremeni nachala vichislenij
StartTime = Timer
' Vipolnenie vichislenij
x = 0
y = 0
For i = 1 To 10000
x = x + 1
y = x + 1
For j = 1 To 10000
A = x + y + i
B = y - x - i
C = x / y * i
Next j
Next i
' Poluchenie vremeni okonchanija vichislenij
EndTime = Timer
' Otobrazhenie obshego vremeni v sekundah
MsgBox Format(EndTime - StartTime, "0.0")
End Sub
Попробуйте запустить сначала такой код, а потом под комментарий добавить начиная со 2 строчки по 5. Просто назначаем тип переменной, а разница обработки по времени в 3 раза. 6,5 сек по сравнению с 17.
В качестве эксперемента изменил переменные на String. Эксель думал 311 секунд.
Код
Dim x As String, y As String
Dim A As String, B As String, C As String
Мораль сей басни такова, чем больше знате, тем быстрее считаете.
ознакомьтесь - тогда откроется еще одна тайна: почему принудительное объявление переменных лучше взять в привычку.
По поводу объявления As String: здесь играет роль не столько отсутствие объявления, сколько неверное объявление. При каждой математической операции VBE пытается преобразовать тип данных к числовому, что отнимает время. А т.к. у Вас все переменные текстовые - то и преобразование будет происходить каждый раз. Время обработки, естественно, увеличивается. Поэтому важно не только объявить переменную, но и объявить правильно(назначить нужный тип данных). Что такое переменная и как правильно её объявить?
Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы...
The_Prist написал: Что такое переменная и как правильно её объявить?
Прочитал статью. Внесу и свои пять копеек. Переменные в VB передаются "по ссылке" (ByRef) и "по значению" (ByVal). Что это значит?
Например, у нас есть две процедуры. В первой мы объявляем переменную "x" и передаём её во вторую процедуру, где прибавляем к "x" единичку.
Код
Sub Test1()
Dim X As Integer
X = 5
Debug.Print "X до изменений: " & X
Call ChangeVar1(X)
Debug.Print "X после до изменений: " & X
End Sub
Sub ChangeVar1(X2 As Integer)
X2 = X2 + 1
End Sub
После запуска Test1 мы увидим, что значение X после передачи в ChangeVar стало 6. Почему? Потому что по умолчанию в VB переменные передаются по ссылке. То есть ChangeVar мы можем переписать на эквивалентную процедуру:
Код
Sub ChangeVar(ByRef X As Integer)
Если мы поменяем "ByRef" на "ByVal":
Код
Sub ChangeVar(ByVal X As Integer)
то переменная X будет по-прежным равным 5. Но... как же так получается? В случае с ByRef мы передаём не саму переменную X, а её адрес. Это называется shallow copy. В случае же с ByVal создаётся новая область в памяти, куда копируется значение "5", и её адрес сохраняется в переменную X2 (deep copy).
Другими словами, переменная - это указатель на адрес в памяти, который хранит значение, а не само значение. Чтобы более наглядно показать это, запуститие у себя процедуру Test:
Код
Sub Test()
Dim X As Integer
X = 5
Debug.Print "адрес X: " & VarPtr(X)
Debug.Print "значение X до изменения: " & X
Call ChangeVar(X)
Debug.Print "значение X после изменения: " & X
Debug.Print "адрес X после изменения: " & VarPtr(X)
End Sub
Sub ChangeVar(ByRef X2 As Integer)
Debug.Print "адрес X2: " & VarPtr(X2)
Debug.Print "значение X2: " & X2
X2 = 7
End Sub
' Результат на моём компьютере (у вас будет другой адрес):
' адрес X: 622373453280
' значение X до изменения: 5
' адрес X2: 622373453280
' значение X2: 5
' значение X после изменения: 7
' адрес X после изменения: 622373453280
Давайте на минуту представим, что переменная хранит значение, а не адрес на значение. Тогда как же так получается, что адреса X и X2 равны??? Две переменные по одному адресу не могут находиться одновременно. VarPtr позволяет нам узнать адрес, который хранится в переменной (также ObjPtr и StrPtr). Как мы видим из результата работы процедуры, переменные X и X2 ссылаются на один и тот же адрес в памяти. Просто VBA скрывает от нас такие подробности. Зная более детально такие механизмы, вы будете лучше понимать, что творится "под капотом".
В C/C++ выражение
Код
Dim x As Integer
x = 2
идентично
Код
int x;
*(&x) = 2;
// "&" берёт адрес, который хранится в x
// "*" ссылка на значение по этому адресу и присваивает значение "2"
А концепция ByRef более понятна:
Код
#include <cwchar>
#include <clocale>
void ChangeByRef(int* y) // "y" - указатель на адрес
{
wprintf(L"размер указателя y: %d байта\n", sizeof(y)); // 4 байта - для x86; 8 байт - для x64
wprintf(L"адрес указателя y: %d, значение y: %d\n", &y, *y);
wprintf(L"адрес, который содержится в указателе: %d\n", y);
*y += 1; // Прибавляем 1 к 5
}
int main()
{
setlocale(LC_ALL, "");
int x;
x = 5;
wprintf(L"адрес переменной x: %d, значение x: %d\n", &x, x);
wprintf(L"вызываем ChangeByRef...\n");
ChangeByRef(&x); // Передаём адрес значения, который содержится в "x"
wprintf(L"адрес переменной x: %d, значение x: %d\n", &x, x);
}
// Вывод программы:
//
// адрес переменной x: 15990336, значение x: 5
// вызываем ChangeByRef...
// размер указателя y: 4 байта
// адрес указателя y: 15990124, значение y: 5
// адрес, который содержится в указателе: 15990336
// адрес переменной x: 15990336, значение x: 6
The_Prist написал: Важен тип передачи и тип самой переменной.
Ещё важно знать тип Variant, который может принимать любой тип (например, Integer, Range, Worksheet, Object). То есть если я точно ожидаю объект Range, то лучше использовать Range. Ну а если я ожидаю разные типы переменных, то тогда Variant (или если будут только объекты, то Object):
Код
Sub Test1()
Const x As Integer = 1
Dim b As MSForms.CommandButton
Call Sub1(b)
Call Sub1(x)
End Sub
Sub Sub1(v As Variant)
End Sub