VBA
Объявление переменных
Поиск…
Неявная и явная декларация
Если модуль кода не содержит Option Explicit
в верхней части модуля, тогда компилятор автоматически (то есть «неявно») создает переменные для вас, когда вы их используете. Они будут использовать переменную типа Variant
.
Public Sub ExampleDeclaration()
someVariable = 10 '
someOtherVariable = "Hello World"
'Both of these variables are of the Variant type.
End Sub
В приведенном выше коде, если указан параметр Option Explicit
, код будет прерываться, потому что ему не нужны необходимые инструкции Dim
для someVariable
и someOtherVariable
.
Option Explicit
Public Sub ExampleDeclaration()
Dim someVariable As Long
someVariable = 10
Dim someOtherVariable As String
someOtherVariable = "Hello World"
End Sub
Рекомендуется использовать Option Explicit в модулях кода, чтобы гарантировать, что вы объявите все переменные.
См. VBA Best Practices, как установить этот параметр по умолчанию.
переменные
Объем
Переменная может быть объявлена (при увеличении уровня видимости):
- На уровне процедуры, используя ключевое слово
Dim
в любой процедуре; локальная переменная . - На уровне модуля, используя ключевое слово
Private
в модуле любого типа; частное поле . - На уровне экземпляра используйте ключевое слово
Friend
в модуле любого типа; поле друга . - На уровне экземпляра, используя ключевое слово
Public
в модуле класса любого типа; публичное поле . - Глобально, используя ключевое слово
Public
в стандартном модуле ; глобальная переменная .
Переменные всегда должны быть объявлены с наименьшей возможной областью: предпочитайте передавать параметры процедурам, а не объявлять глобальные переменные.
Дополнительную информацию см. В разделе Модификаторы доступа .
Местные переменные
Используйте ключевое слово Dim
чтобы объявить локальную переменную :
Dim identifierName [As Type][, identifierName [As Type], ...]
Параметр [As Type]
синтаксиса объявления является необязательным. Когда указано, он устанавливает тип данных переменной, который определяет, сколько памяти будет выделено этой переменной. Это объявляет переменную String
:
Dim identifierName As String
Если тип не указан, тип неявно Variant
:
Dim identifierName 'As Variant is implicit
Синтаксис VBA также поддерживает объявление нескольких переменных в одном выражении:
Dim someString As String, someVariant, someValue As Long
Обратите внимание, что [As Type]
должен быть указан для каждой переменной (кроме «Variant»). Это относительно распространенная ловушка:
Dim integer1, integer2, integer3 As Integer 'Only integer3 is an Integer.
'The rest are Variant.
Статические переменные
Локальные переменные также могут быть Static
. В VBA ключевое слово Static
используется для того, чтобы переменная «запомнила» значение, которое она имела, в последний раз, когда была вызвана процедура:
Private Sub DoSomething()
Static values As Collection
If values Is Nothing Then
Set values = New Collection
values.Add "foo"
values.Add "bar"
End If
DoSomethingElse values
End Sub
Здесь коллекция values
объявляется как Static
локальная; потому что это переменная объекта , она инициализируется Nothing
. Условие, следующее за объявлением, проверяет, была ли ссылка на объект Set
раньше - если она выполняется в первый раз, процедура инициализируется. DoSomethingElse
может добавлять или удалять элементы, и они все равно будут в коллекции в следующий раз, когда вызывается DoSomething
.
альтернатива
Ключевое слово VBA
Static
легко может быть неправильно понято - особенно опытные программисты, которые обычно работают на других языках. На многих языкахstatic
используется для того, чтобы член класса (field, property, method, ...) принадлежал типу, а не экземпляру . Код вstatic
контексте не может ссылаться на код в контексте экземпляра . Ключевое слово VBAStatic
означает что-то совершенно другое.
Часто Static
локальный объект может быть также реализован как Private
переменная (поле) на уровне модуля, однако это бросает вызов принципу, с помощью которого переменная должна быть объявлена с наименьшей возможной областью; доверяйте своим инстинктам, используйте то, что вы предпочитаете - оба будут работать ... но использование Static
без понимания того, что он делает, может привести к интересным ошибкам.
Дим против частного
Ключевое слово Dim
является законным на уровне процедур и модулей; его использование на уровне модуля эквивалентно использованию ключевого слова Private
:
Option Explicit
Dim privateField1 As Long 'same as Private privateField2 as Long
Private privateField2 As Long 'same as Dim privateField2 as Long
Ключевое слово Private
доступно только на уровне модуля; это вызывает резервирование Dim
для локальных переменных и объявление переменных модуля с помощью Private
, особенно с контрастным ключевым словом Public
которое должно быть использовано для объявления публичного участника. Также можно использовать Dim
везде - то , что имеет значение консистенция:
«Частные поля»
- DO использовать
Private
чтобы объявить переменную уровня модуля. - DO используйте
Dim
чтобы объявить локальную переменную. - НЕ используйте
Dim
чтобы объявить переменную уровня модуля.
«Тусклый везде»
- DO используйте
Dim
чтобы объявить что-либо частным / местным. - НЕ используйте
Private
чтобы объявить переменную уровня модуля. - ИЗБЕГАЙТЕ объявление
Public
полей. *
* В общем, следует избегать объявления Public
или Global
полей.
поля
Переменная, объявленная на уровне модуля, в разделе объявлений в верхней части тела модуля является полем . Public
поле, объявленное в стандартном модуле, является глобальной переменной :
Public PublicField As Long
Доступ к переменной с глобальной областью можно получить из любого места, включая другие проекты VBA, которые будут ссылаться на проект, в котором он объявлен.
Чтобы сделать переменную global / public, но только видимую изнутри проекта, используйте модификатор Friend
:
Friend FriendField As Long
Это особенно полезно в надстройках, где намерение заключается в том, что другие проекты VBA ссылаются на проект надстройки и могут потреблять открытый API.
Friend FriendField As Long 'public within the project, aka for "friend" code
Public PublicField As Long 'public within and beyond the project
Поля друзей недоступны в стандартных модулях.
Поля экземпляра
Переменная, объявленная на уровне модуля, в разделе объявлений в верхней части тела модуля класса (включая ThisWorkbook
, ThisDocument
, Worksheet
, UserForm
и модулях классов ) является полем экземпляра : оно существует только до тех пор, пока существует экземпляр класс вокруг.
'> Class1
Option Explicit
Public PublicField As Long
'> Module1
Option Explicit
Public Sub DoSomething()
'Class1.PublicField means nothing here
With New Class1
.PublicField = 42
End With
'Class1.PublicField means nothing here
End Sub
Инкапсулирующие поля
Данные Instance часто хранится Private
и дублирован инкапсулируются. Закрытое поле можно открыть с помощью процедуры Property
. Чтобы публично публиковать приватную переменную, не предоставляя доступ к записи вызывающему, модуль класса (или стандартный модуль) реализует элемент « Property Get
:
Option Explicit
Private encapsulated As Long
Public Property Get SomeValue() As Long
SomeValue = encapsulated
End Property
Public Sub DoSomething()
encapsulated = 42
End Sub
Сам класс может изменить инкапсулированное значение, но вызывающий код может получить доступ только к Public
членам (и членам Friend
, если вызывающий объект находится в одном проекте).
Чтобы разрешить вызывающему абоненту:
- Инкапсулированное значение , модуль предоставляет элемент
Property Let
. - Ссылка на инкапсулированные объекты , модуль предоставляет член
Property Set
.
Константы (Const)
Если у вас есть значение, которое никогда не изменяется в вашем приложении, вы можете определить именованную константу и использовать ее вместо буквенного значения.
Вы можете использовать Const только на уровне модуля или процедуры. Это означает, что контекст объявления для переменной должен быть классом, структурой, модулем, процедурой или блоком и не может быть исходным файлом, пространством имен или интерфейсом.
Public Const GLOBAL_CONSTANT As String = "Project Version #1.000.000.001"
Private Const MODULE_CONSTANT As String = "Something relevant to this Module"
Public Sub ExampleDeclaration()
Const SOME_CONSTANT As String = "Hello World"
Const PI As Double = 3.141592653
End Sub
Хотя можно считать хорошей практикой указывать константные типы, это строго не требуется. Не указывая тип, он все равно приведет к правильному типу:
Public Const GLOBAL_CONSTANT = "Project Version #1.000.000.001" 'Still a string
Public Sub ExampleDeclaration()
Const SOME_CONSTANT = "Hello World" 'Still a string
Const DERIVED_CONSTANT = SOME_CONSTANT 'DERIVED_CONSTANT is also a string
Const VAR_CONSTANT As Variant = SOME_CONSTANT 'VAR_CONSTANT is Variant/String
Const PI = 3.141592653 'Still a double
Const DERIVED_PI = PI 'DERIVED_PI is also a double
Const VAR_PI As Variant = PI 'VAR_PI is Variant/Double
End Sub
Обратите внимание, что это специфично для констант и в отличие от переменных, где не указано, что тип приводит к типу Variant.
Хотя можно явно объявить константу как String, невозможно объявить константу в виде строки с использованием синтаксиса строки фиксированной ширины
'This is a valid 5 character string constant
Const FOO As String = "ABCDE"
'This is not valid syntax for a 5 character string constant
Const FOO As String * 5 = "ABCDE"
Модификаторы доступа
Dim
должна быть зарезервирована для локальных переменных. На уровне модуля предпочитайте явные модификаторы доступа:
-
Private
для частных полей, доступ к которым возможен только в том модуле, в котором они объявлены. -
Public
для общедоступных полей и глобальных переменных, к которым может обращаться любой вызывающий код. -
Friend
для переменных, публикуемых в рамках проекта, но недоступных для других проектов VBA, связанных с надстройками (для надстроек) -
Global
также может использоваться дляPublic
полей в стандартных модулях, но является незаконным в модулях классов и в любом случае является устаревшим - вместо этого предпочитайтеPublic
модификатор. Этот модификатор также не является законным для процедур.
Модификаторы доступа применимы как к переменным, так и к процедурам.
Private ModuleVariable As String
Public GlobalVariable As String
Private Sub ModuleProcedure()
ModuleVariable = "This can only be done from within the same Module"
End Sub
Public Sub GlobalProcedure()
GlobalVariable = "This can be done from any Module within this Project"
End Sub
Дополнительный модуль
Открытые Sub
процедуры в стандартных модулях отображаются как макросы и могут быть прикреплены к элементам управления и сочетания клавиш в документе хоста.
И наоборот, публичные процедуры Function
в стандартных модулях отображаются как пользовательские функции (UDF) в хост-приложении.
Указание отдельного модуля Option Private Module
в верхней части стандартного модуля не позволяет его членам отображаться как макросы и UDF для приложения-хозяина.
Тип подсказки
Тип подсказки сильно обескуражен. Они существуют и документируются здесь по причинам исторической и обратной совместимости. As [DataType]
этого следует использовать синтаксис As [DataType]
.
Public Sub ExampleDeclaration()
Dim someInteger% '% Equivalent to "As Integer"
Dim someLong& '& Equivalent to "As Long"
Dim someDecimal@ '@ Equivalent to "As Currency"
Dim someSingle! '! Equivalent to "As Single"
Dim someDouble# '# Equivalent to "As Double"
Dim someString$ '$ Equivalent to "As String"
Dim someLongLong^ '^ Equivalent to "As LongLong" in 64-bit VBA hosts
End Sub
Типовые подсказки значительно уменьшают читаемость кода и поощряют венгерскую нотацию, которая также препятствует читаемости:
Dim strFile$
Dim iFile%
Вместо этого, объявляйте переменные ближе к их использованию и называйте вещи тем, что они используются, а не после их типа:
Dim path As String
Dim handle As Integer
Типовые подсказки также могут использоваться в литералах, чтобы обеспечить соблюдение определенного типа. По умолчанию числовой литерал, меньший, чем 32 768, будет интерпретироваться как литерал Integer
, но с подсказкой типа вы можете контролировать это:
Dim foo 'implicit Variant
foo = 42& ' foo is now a Long
foo = 42# ' foo is now a Double
Debug.Print TypeName(42!) ' prints "Single"
Типные подсказки обычно не нужны для литералов, потому что они будут назначены переменной, объявленной с явным типом, или неявно преобразуются в соответствующий тип при передаче в качестве параметров. Неявные преобразования можно избежать, используя одну из явных функций преобразования типов:
'Calls procedure DoSomething and passes a literal 42 as a Long using a type hint
DoSomething 42&
'Calls procedure DoSomething and passes a literal 42 explicitly converted to a Long
DoSomething CLng(42)
Строковые возвращаемые встроенные функции
Большинство встроенных функций, которые обрабатывают строки, представлены в двух версиях: свободно напечатанная версия, которая возвращает Variant
, и строго типизированную версию (заканчивающуюся на $
), которая возвращает String
. Если вы не назначаете возвращаемое значение для Variant
, вы должны предпочесть версию, которая возвращает String
противном случае происходит неявное преобразование возвращаемого значения.
Debug.Print Left(foo, 2) 'Left returns a Variant
Debug.Print Left$(foo, 2) 'Left$ returns a String
Эти функции:
- VBA.Conversion.Error -> VBA.Conversion.Error $
- VBA.Conversion.Hex -> VBA.Conversion.Hex $
- VBA.Conversion.Oct -> VBA.Conversion.Oct $
- VBA.Conversion.Str -> VBA.Conversion.Str $
- VBA.FileSystem.CurDir -> VBA.FileSystem.CurDir $
- VBA. [_ HiddenModule] .Input -> VBA. [_ HiddenModule] .Input $
- VBA. [_ HiddenModule] .InputB -> VBA. [_ HiddenModule] .InputB $
- VBA.Interaction.Command -> VBA.Interaction.Command $
- VBA.Interaction.Environ -> VBA.Interaction.Environ $
- VBA.Strings.Chr -> VBA.Strings.Chr $
- VBA.Strings.ChrB -> VBA.Strings.ChrB $
- VBA.Strings.ChrW -> VBA.Strings.ChrW $
- VBA.Strings.Format -> VBA.Strings.Format $
- VBA.Strings.LCase -> VBA.Strings.LCase $
- VBA.Strings.Left -> VBA.Strings.Left $
- VBA.Strings.LeftB -> VBA.Strings.LeftB $
- VBA.Strings.LTtrim -> VBA.Strings.LTrim $
- VBA.Strings.Mid -> VBA.Strings.Mid $
- VBA.Strings.MidB -> VBA.Strings.MidB $
- VBA.Strings.Right -> VBA.Strings.Right $
- VBA.Strings.RightB -> VBA.Strings.RightB $
- VBA.Strings.RTrim -> VBA.Strings.RTrim $
- VBA.Strings.Space -> VBA.Strings.Space $
- VBA.Strings.Str -> VBA.Strings.Str $
- VBA.Strings.String -> VBA.Strings.String $
- VBA.Strings.Trim -> VBA.Strings.Trim $
- VBA.Strings.UCase -> VBA.Strings.UCase $
Обратите внимание, что это псевдонимы функций, не совсем намеки на тип . Функция Left
соответствует скрытой функции B_Var_Left
, а версия Left$
соответствует скрытой функции B_Str_Left
.
В ранних версиях VBA знак $
не является допустимым символом, а имя функции должно быть заключено в квадратные скобки. В Word Basic было много и много функций, которые возвращали строки, которые заканчивались в $.
Объявление строк фиксированной длины
В VBA строки могут быть объявлены с определенной длиной; они автоматически дополняются или усекаются, чтобы поддерживать указанную длину.
Public Sub TwoTypesOfStrings()
Dim FixedLengthString As String * 5 ' declares a string of 5 characters
Dim NormalString As String
Debug.Print FixedLengthString ' Prints " "
Debug.Print NormalString ' Prints ""
FixedLengthString = "123" ' FixedLengthString now equals "123 "
NormalString = "456" ' NormalString now equals "456"
FixedLengthString = "123456" ' FixedLengthString now equals "12345"
NormalString = "456789" ' NormalString now equals "456789"
End Sub
Когда использовать статическую переменную
Статическая переменная, объявленная локально, не разрушается и не теряет своего значения при выходе из процедуры Sub. Последующие вызовы процедуры не требуют повторной инициализации или присвоения, хотя вы можете «нулевать» любые запоминаемые значения.
Они особенно полезны при позднем связывании объекта в вспомогательном подэлементе, который вызывается повторно.
Фрагмент 1: повторное использование объекта Scripting.Dictionary на многих листах
Option Explicit
Sub main()
Dim w As Long
For w = 1 To Worksheets.Count
processDictionary ws:=Worksheets(w)
Next w
End Sub
Sub processDictionary(ws As Worksheet)
Dim i As Long, rng As Range
Static dict As Object
If dict Is Nothing Then
'initialize and set the dictionary object
Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Else
'remove all pre-existing dictionary entries
' this may or may not be desired if a single dictionary of entries
' from all worksheets is preferred
dict.RemoveAll
End If
With ws
'work with a fresh dictionary object for each worksheet
' without constructing/destructing a new object each time
' or do not clear the dictionary upon subsequent uses and
' build a dictionary containing entries from all worksheets
End With
End Sub
Snippet 2: создать рабочий лист UDF, который опоздает с привязкой к объекту VBScript.RegExp
Option Explicit
Function numbersOnly(str As String, _
Optional delim As String = ", ")
Dim n As Long, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once
'this is beneficial when filling a long column with this UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
Else
Set cmat = Nothing
End If
With rgx
.Global = True
.MultiLine = True
.Pattern = "[0-9]{1,999}"
If .Test(str) Then
Set cmat = .Execute(str)
'resize the nums array to accept the matches
ReDim nums(cmat.Count - 1)
'populate the nums array with the matches
For n = LBound(nums) To UBound(nums)
nums(n) = cmat.Item(n)
Next n
'convert the nums array to a delimited string
numbersOnly = Join(nums, delim)
Else
numbersOnly = vbNullString
End If
End With
End Function
Пример UDF со статическим объектом, заполненным полмиллиона строк
* Истекшее время, чтобы заполнить 500K строк с помощью UDF: - с Dim rgx As Object : 148.74 секунд - с Static rgx As Object : 26.07 секунд * Они должны учитываться только для относительного сравнения. Ваши собственные результаты будут варьироваться в зависимости от сложности и
объем выполненных операций.
Помните, что UDF не рассчитывается один раз в жизни книги. Даже энергонезависимый UDF будет пересчитываться всякий раз, когда значения в пределах диапазона (ов), которые он ссылается, могут быть изменены. Каждое последующее событие пересчета только увеличивает преимущества статически объявленной переменной.
- Статическая переменная доступна для времени жизни модуля, а не для процедуры или функции, в которой она была объявлена и назначена.
- Статические переменные могут быть объявлены только локально.
- Статическая переменная содержит много одинаковых свойств переменной уровня частного модуля, но с более ограниченной областью.
Ссылки по теме: Static (Visual Basic)