VBA
Declarando variables
Buscar..
Declaración implícita y explícita
Si un módulo de código no contiene Option Explicit
en la parte superior del módulo, entonces el compilador creará automáticamente (es decir, "implícitamente") variables cuando los use. Ellos por defecto serán de tipo variable Variant
.
Public Sub ExampleDeclaration()
someVariable = 10 '
someOtherVariable = "Hello World"
'Both of these variables are of the Variant type.
End Sub
En el código anterior, si se especifica Option Explicit
, el código se interrumpirá porque faltan las sentencias Dim
requeridas para someVariable
y someOtherVariable
.
Option Explicit
Public Sub ExampleDeclaration()
Dim someVariable As Long
someVariable = 10
Dim someOtherVariable As String
someOtherVariable = "Hello World"
End Sub
Se considera una buena práctica usar Option Explicit en los módulos de código, para garantizar que declare todas las variables.
Vea VBA Best Practices cómo configurar esta opción por defecto.
Variables
Alcance
Se puede declarar una variable (en aumento del nivel de visibilidad):
- A nivel de procedimiento, usando la palabra clave
Dim
en cualquier procedimiento; una variable local . - A nivel de módulo, usando la palabra clave
Private
en cualquier tipo de módulo; Un campo privado . - A nivel de instancia, utilizando la palabra clave
Friend
en cualquier tipo de módulo de clase; un campo de amigos - A nivel de instancia, utilizando la palabra clave
Public
en cualquier tipo de módulo de clase; Un campo público . - A nivel mundial, utilizando la palabra clave
Public
en un módulo estándar ; una variable global .
Las variables siempre deben declararse con el menor alcance posible: prefiera pasar parámetros a procedimientos, en lugar de declarar variables globales.
Ver modificadores de acceso para más información.
Variables locales
Use la palabra clave Dim
para declarar una variable local :
Dim identifierName [As Type][, identifierName [As Type], ...]
La parte [As Type]
de la sintaxis de la declaración es opcional. Cuando se especifica, establece el tipo de datos de la variable, que determina cuánta memoria se asignará a esa variable. Esto declara una variable de String
:
Dim identifierName As String
Cuando no se especifica un tipo, el tipo es implícitamente Variant
:
Dim identifierName 'As Variant is implicit
La sintaxis de VBA también admite la declaración de múltiples variables en una sola declaración:
Dim someString As String, someVariant, someValue As Long
Observe que se debe especificar [As Type]
para cada variable (que no sean las de 'Variante'). Esta es una trampa relativamente común:
Dim integer1, integer2, integer3 As Integer 'Only integer3 is an Integer.
'The rest are Variant.
Variables estáticas
Las variables locales también pueden ser Static
. En VBA, la palabra clave Static
se usa para hacer que una variable "recuerde" el valor que tenía, la última vez que se llamó a un procedimiento:
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
Aquí la colección de values
se declara como un Static
local; Debido a que es una variable de objeto , se inicializa en Nothing
. La condición que sigue a la declaración verifica si la referencia del objeto se Set
antes: si es la primera vez que se ejecuta el procedimiento, la colección se inicializa. DoSomethingElse
podría estar agregando o eliminando elementos, y aún estarán en la colección la próxima vez que se DoSomething
.
Alternativa
La palabra clave
Static
de VBA se puede malinterpretar fácilmente, especialmente por programadores experimentados que generalmente trabajan en otros idiomas. En muchos idiomas,static
se utiliza para hacer que un miembro de clase (campo, propiedad, método, ...) pertenezca al tipo en lugar de a la instancia . El código en contextostatic
no puede hacer referencia al código en contexto de instancia . La palabra claveStatic
VBA significa algo muy diferente.
A menudo, un local Static
podría implementarse como una variable (campo) Private
a nivel de módulo; sin embargo, esto pone en tela de juicio el principio por el cual una variable debe declararse con el menor alcance posible; confíe en sus instintos, use el que prefiera, ambos funcionarán ... pero usar Static
sin entender lo que hace podría provocar errores interesantes.
Dim vs. Private
La palabra clave Dim
es legal en los niveles de procedimiento y módulo; su uso a nivel de módulo es equivalente a usar la palabra clave Private
:
Option Explicit
Dim privateField1 As Long 'same as Private privateField2 as Long
Private privateField2 As Long 'same as Dim privateField2 as Long
La palabra clave Private
solo es legal a nivel de módulo; esto invita a reservar Dim
para variables locales y declarar variables de módulo con Private
, especialmente con la palabra clave Public
contrastante que debería usarse de todos modos para declarar un miembro público. Alternativamente, use Dim
todas partes , lo que importa es la consistencia :
"Campos privados"
- Use el
Private
para declarar una variable de nivel de módulo. - Use
Dim
para declarar una variable local. - NO use
Dim
para declarar una variable de nivel de módulo.
"Oscuro en todas partes"
- Use
Dim
para declarar cualquier cosa privada / local. - NO use
Private
para declarar una variable de nivel de módulo. - EVITA declarar campos
Public
. *
* En general, se debe evitar declarar campos Public
o Global
todos modos.
Campos
Una variable declarada a nivel de módulo, en la sección de declaraciones en la parte superior del cuerpo del módulo, es un campo . Un campo Public
declarado en un módulo estándar es una variable global :
Public PublicField As Long
Se puede acceder a una variable con un alcance global desde cualquier lugar, incluidos otros proyectos de VBA que hagan referencia al proyecto en el que se ha declarado.
Para hacer una variable global / pública, pero solo visible desde el proyecto, use el modificador de Friend
:
Friend FriendField As Long
Esto es especialmente útil en los complementos, donde la intención es que otros proyectos de VBA hagan referencia al proyecto del complemento y puedan consumir la API pública.
Friend FriendField As Long 'public within the project, aka for "friend" code
Public PublicField As Long 'public within and beyond the project
Los campos de amigos no están disponibles en módulos estándar.
Campos de instancia
Una variable declarada a nivel de módulo, en la sección de declaraciones en la parte superior del cuerpo de un módulo de clase (incluyendo ThisWorkbook
, ThisDocument
, Worksheet
, UserForm
y módulos de clase ), es un campo de instancia : solo existe mientras exista una instancia de la clase alrededor
'> 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
Campos de encapsulacion
Los datos de instancia a menudo se mantienen Private
, y doblados encapsulados . Un campo privado puede ser expuesto usando un procedimiento de Property
. Para exponer públicamente una variable privada sin otorgar acceso de escritura al llamante, un módulo de clase (o un módulo estándar) implementa un miembro de 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
La clase en sí puede modificar el valor encapsulado, pero el código de llamada solo puede acceder a los miembros Public
(y miembros de Friend
, si la persona que llama está en el mismo proyecto).
Para permitir que la persona que llama modifique:
- Un valor encapsulado, un módulo expone un miembro
Property Let
. - Una referencia de objeto encapsulada, un módulo expone un miembro del
Property Set
.
Constantes (const)
Si tiene un valor que nunca cambia en su aplicación, puede definir una constante con nombre y usarla en lugar de un valor literal.
Puede utilizar Const solo a nivel de módulo o procedimiento. Esto significa que el contexto de declaración para una variable debe ser una clase, estructura, módulo, procedimiento o bloque, y no puede ser un archivo de origen, espacio de nombres o interfaz.
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
Si bien puede considerarse una buena práctica especificar tipos de constantes, no es estrictamente necesario. Si no especifica el tipo, se obtendrá el tipo correcto:
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
Tenga en cuenta que esto es específico de las Constantes y en contraste con las variables donde no se especifica el tipo de resultado en un tipo Variant.
Si bien es posible declarar explícitamente una constante como una cadena, no es posible declarar una constante como una cadena usando una sintaxis de cadena de ancho fijo
'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"
Modificadores de acceso
La declaración Dim
debe estar reservada para las variables locales. A nivel de módulo, prefiera modificadores de acceso explícitos:
-
Private
para campos privados, al que solo se puede acceder dentro del módulo en el que están declarados. -
Public
para campos públicos y variables globales, al que se puede acceder mediante cualquier código de llamada. -
Friend
para variables públicas dentro del proyecto, pero inaccesible para otros proyectos VBA de referencia (relevantes para complementos) -
Global
también se puede usar para camposPublic
en módulos estándar, pero es ilegal en módulos de clase y está obsoleto de todos modos, prefiera el modificadorPublic
lugar. Este modificador tampoco es legal para los procedimientos.
Los modificadores de acceso son aplicables a variables y procedimientos por igual.
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
Opción Módulo Privado
Los procedimientos Sub
parámetros públicos en módulos estándar se exponen como macros y se pueden adjuntar a controles y atajos de teclado en el documento host.
A la inversa, los procedimientos de Function
públicas en módulos estándar se exponen como funciones definidas por el usuario (UDF) en la aplicación host.
La especificación del Option Private Module
en la parte superior de un módulo estándar evita que sus miembros se vean expuestos como macros y UDF a la aplicación host.
Tipo de sugerencias
Las sugerencias de tipo están muy desanimadas. Existen y se documentan aquí por razones históricas y de compatibilidad con versiones anteriores. Debería usar la sintaxis As [DataType]
lugar.
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
Las sugerencias de tipo disminuyen significativamente la legibilidad del código y fomentan una notación húngara heredada que también dificulta la legibilidad:
Dim strFile$
Dim iFile%
En su lugar, declare las variables más cerca de su uso y nombre las cosas por lo que usan, no por su tipo:
Dim path As String
Dim handle As Integer
Las sugerencias de tipo también se pueden usar en literales, para imponer un tipo específico. De forma predeterminada, un literal numérico menor que 32,768 se interpretará como un literal Integer
, pero con una sugerencia de tipo puede controlar que:
Dim foo 'implicit Variant
foo = 42& ' foo is now a Long
foo = 42# ' foo is now a Double
Debug.Print TypeName(42!) ' prints "Single"
Las sugerencias de tipo generalmente no son necesarias en los literales, ya que se asignarían a una variable declarada con un tipo explícito, o se convertirían implícitamente al tipo apropiado cuando se pasaran como parámetros. Las conversiones implícitas se pueden evitar usando una de las funciones explícitas de conversión de tipos:
'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)
Funciones incorporadas de retorno de cadena
La mayoría de las funciones integradas que manejan cadenas vienen en dos versiones: una versión escrita de forma holgada que devuelve una Variant
y una versión fuertemente tipada (que termina con $
) que devuelve una String
. A menos que esté asignando el valor de retorno a una Variant
, debe preferir la versión que devuelve una String
; de lo contrario, hay una conversión implícita del valor de retorno.
Debug.Print Left(foo, 2) 'Left returns a Variant
Debug.Print Left$(foo, 2) 'Left$ returns a String
Estas funciones son:
- 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 $
Tenga en cuenta que estos son alias de funciones, no son exactamente sugerencias de tipo . La función Left
corresponde a la función oculta B_Var_Left
, mientras que la versión Left$
corresponde a la función oculta B_Str_Left
.
En versiones muy tempranas de VBA, el signo $
no es un carácter permitido y el nombre de la función tenía que estar entre corchetes. En Word Basic, había muchas, muchas más funciones que devolvían cadenas que terminaban en $.
Declarar cadenas de longitud fija
En VBA, las cadenas se pueden declarar con una longitud específica; se rellenan o truncan automáticamente para mantener esa longitud según lo declarado.
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
Cuándo usar una variable estática
Una variable estática declarada localmente no se destruye y no pierde su valor cuando se sale del procedimiento Sub. Las llamadas subsiguientes al procedimiento no requieren reinicialización o asignación, aunque es posible que desee "cero" cualquier valor (s) recordado (s).
Estos son particularmente útiles cuando se vincula tarde un objeto en un sub 'auxiliar' que se llama repetidamente.
Fragmento 1: reutilizar un objeto Scripting.Dictionary en muchas hojas de trabajo
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
Fragmento 2: cree un UDF de hoja de cálculo que tarde enlaza el objeto 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
Ejemplo de UDF con objeto estático rellenado a través de medio millón de filas
* Tiempos transcurridos para llenar 500K filas con UDF: - con Dim rgx como objeto : 148.74 segundos - con rgx estático como objeto : 26.07 segundos * Estos deben ser considerados para comparación relativa solamente. Sus propios resultados variarán según la complejidad y
Alcance de las operaciones realizadas.
Recuerde que un UDF no se calcula una vez en la vida útil de un libro de trabajo. Incluso un UDF no volátil se volverá a calcular siempre que los valores dentro del rango (s) a los que hace referencia estén sujetos a cambios. Cada evento de recálculo posterior solo aumenta los beneficios de una variable declarada estáticamente.
- Una variable estática está disponible durante la vida útil del módulo, no el procedimiento o la función en la que se declaró y asignó.
- Las variables estáticas solo pueden ser declaradas localmente.
- La variable estática contiene muchas de las mismas propiedades de una variable de nivel de módulo privado pero con un alcance más restringido.
Referencia relacionada: Estático (Visual Basic)