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 contexto static no puede hacer referencia al código en contexto de instancia . La palabra clave Static 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 campos Public en módulos estándar, pero es ilegal en módulos de clase y está obsoleto de todos modos, prefiera el modificador Public 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

static_UDF
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)



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow