sql >> Base de Datos >  >> RDS >> Access

Función de cambio de caso de VBA

Como firme defensor del control de versiones en Microsoft Access, debo hablar sobre mi mayor queja con el entorno de desarrollo de VBA:el "reencuadre" automático de los identificadores. Piense en esto como una expansión de mi respuesta a una pregunta sobre esta "característica" en stackoverflow.

Voy a abordar este artículo en dos partes. En la Parte 1, definiré el comportamiento del entorno de desarrollo. En la Parte 2, discutiré mi teoría sobre por qué funciona de esta manera.

Parte 1:Definiendo el comportamiento

Si ha pasado algún tiempo escribiendo código en VBA, estoy seguro de que ha notado esta "característica". A medida que escribe identificadores (variables, nombres de funciones, enumeraciones, etc.), puede notar que el IDE cambia automáticamente las mayúsculas y minúsculas de estos identificadores. Por ejemplo, puede escribir el nombre de una variable en minúsculas, pero tan pronto como pasa a una nueva línea, la primera letra de su variable  cambia repentinamente a mayúsculas.

La primera vez que ves esto puede ser discordante. A medida que continúa programando, el IDE continúa cambiando el caso aparentemente al azar. Pero, si pasa suficiente tiempo en el IDE, eventualmente el patrón se revelará.

Para evitar que tenga que pasar más de diez años de su vida esperando que el patrón se le revele, ahora describiré el patrón tal como lo he llegado a entender. Que yo sepa, Microsoft nunca ha documentado oficialmente ninguno de estos comportamientos.

  1. Todos los cambios automáticos de casos son globales para el proyecto de VBA.
  2. Cada vez que se cambia la línea de declaración de cualquiera de los siguientes tipos de identificadores, todos los demás identificadores con el mismo nombre también cambian de mayúsculas y minúsculas:
    • Subnombre
    • Nombre de la función
    • Escriba el nombre
    • Nombre de enumeración
    • Nombre de la variable
    • Nombre constante
    • Nombre de la propiedad
  3. Cada vez que se cambia el nombre de un elemento de enumeración en cualquier parte del código, las mayúsculas y minúsculas del nombre del elemento de enumeración se actualizan para que coincidan en todas partes.

Hablemos ahora de cada uno de estos comportamientos con un poco más de detalle.

Cambios globales

Como escribí anteriormente, los cambios de caso del identificador son globales para un proyecto de VBA. En otras palabras, el IDE de VBA ignora por completo el alcance al cambiar el caso de los identificadores.

Por ejemplo, supongamos que tiene una función privada llamada AccountIsActive en un módulo estándar. Ahora, imagine un módulo de clase en otro lugar de ese mismo proyecto. El módulo de clase tiene un procedimiento de obtención de propiedad privado. Dentro de ese procedimiento Property Get hay una variable local llamada accountIsActive . Tan pronto como escriba la línea Dim accountIsActive As Boolean en el IDE de VBA y pase a una nueva línea, la función La cuenta está activa que definimos por separado en su propio módulo estándar tiene su línea de declaración cambiada a Private Function accountIsActive() para que coincida con la variable local dentro de este módulo de clase.

Eso es un bocado, así que déjame demostrarlo mejor en código.

Paso 1:Definir la función AccountIsActive

'--== Module1 ==--
Private Function AccountIsActive() As Boolean
End Function

Paso 2:Declarar la variable local accountIsActive en un alcance diferente

'--== Class1 ==--
Private Sub Foo()
    Dim accountIsACTIVE As Boolean
End Sub

Paso 3:VBA IDE... ¡¿Qué has hecho?!?!

'--== Module1 ==--
Private Function accountIsACTIVE() As Boolean
End Function

Política de no discriminación de VBA Case-Obliteration

No contento con simplemente ignorar el alcance, VBA también ignora las diferencias entre los tipos de identificadores en su búsqueda por imponer la coherencia entre mayúsculas y minúsculas. En otras palabras, cada vez que declara una nueva función, subrutina o variable que utiliza un nombre de identificador existente, todas las demás instancias de ese identificador cambian de mayúsculas y minúsculas para que coincidan.

En cada uno de estos ejemplos a continuación, lo único que estoy cambiando es el primer módulo de la lista. El IDE de VBA es responsable de todos los demás cambios en los módulos definidos previamente.

Paso 1:Definir una función

'--== Module1 ==--
Public Function ReloadDBData() As Boolean
End Function

Paso 2:Defina un sub con el mismo nombre

NOTA:Esto es perfectamente válido siempre y cuando los trámites estén en módulos diferentes. Dicho esto, solo porque *puedas* hacer algo, no significa que *debas*. Y *debe* evitar esta situación en la medida de lo posible.

'--== Module2 ==--
Public Sub ReloadDbData()
End Sub

'--== Module1 ==--
Public Function ReloadDbData() As Boolean
End Sub

Paso 3:Defina un tipo con el mismo nombre

NOTA:Una vez más, no defina un sub, función y escriba todo con el mismo nombre en un solo proyecto.

'--== Module3 ==--
Private Type ReLoadDBData
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub ReLoadDBData()
End Sub

'--== Module1 ==--
Public Function ReLoadDBData() As Boolean
End Sub

Paso 4:Defina una enumeración con el mismo nombre

NOTA:Por favor, por favor, por amor a todas las cosas santas...

'--== Module4 ==--
Public Enum ReloadDbDATA
    Dummy
End Enum

'--== Module3 ==--
Private Type ReloadDbDATA
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub ReloadDbDATA()
End Sub

'--== Module1 ==--
Public Function ReloadDbDATA() As Boolean
End Sub

Paso 5:Defina una variable con el mismo nombre

NOTA:¿Todavía estamos haciendo esto?

'--== Module5 ==--
Public reloaddbdata As Boolean

'--== Module4 ==--
Public Enum reloaddbdata
    Dummy
End Enum

'--== Module3 ==--
Private Type reloaddbdata
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub reloaddbdata()
End Sub

'--== Module1 ==--
Public Function reloaddbdata() As Boolean
End Sub

Paso 6:Definir una constante con el mismo nombre

NOTA:Oh, vamos. ¿En serio?

'--== Module6 ==--
Private Const RELOADDBDATA As Boolean = True

'--== Module5 ==--
Public RELOADDBDATA As Boolean

'--== Module4 ==--
Public Enum RELOADDBDATA
    Dummy
End Enum

'--== Module3 ==--
Private Type RELOADDBDATA
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub RELOADDBDATA()
End Sub

'--== Module1 ==--
Public Function RELOADDBDATA() As Boolean
End Sub

Paso 7:Defina una propiedad de clase con el mismo nombre

NOTA:Esto se está volviendo tonto.

'--== Class1 ==--
Private Property Get reloadDBData() As Boolean
End Property

'--== Module6 ==--
Private Const reloadDBData As Boolean = True

'--== Module5 ==--
Public reloadDBData As Boolean

'--== Module4 ==--
Public Enum reloadDBData
    Dummy
End Enum

'--== Module3 ==--
Private Type reloadDBData
    Dummy As Variant
End Type

'--== Module2 ==--
Public Sub reloadDBData()
End Sub

'--== Module1 ==--
Public Function reloadDBData() As Boolean
End Sub

¡¿Enumerar elementos?!?!

Para este tercer punto, es importante distinguir entre un tipo Enum y un elemento de enumeración .

Enum EnumTypeName   ' <-- Enum type
    EnumItemAlice   ' <-- Enum item
    EnumItemBob     ' <-- Enum item
End Enum

Ya mostramos anteriormente que los tipos Enum se tratan igual que otros tipos de declaraciones, como subs, funciones, constantes y variables. Cada vez que se cambia la línea de declaración de un identificador con ese nombre, se actualizan las mayúsculas y minúsculas de todos los demás identificadores del proyecto con el mismo nombre para que coincidan con el último cambio.

Enumerar elementos son especiales porque son el único tipo de identificador cuyas mayúsculas y minúsculas se pueden cambiar cada vez que cualquier línea de código que contiene el nombre del elemento de enumeración se cambia.

Paso 1. Defina y complete la enumeración

'--== Module7 ==--
Public Enum EnumTypeName
    EnumItemAlice
    EnumItemBob
End Enum

Paso 2. Consulte los elementos de enumeración en el código

'--== Module8 ==--
Sub TestEnum()
    Debug.Print EnumItemALICE, EnumItemBOB
End Sub

Resultado:la declaración de tipo de enumeración cambia para coincidir con la línea de código normal

'--== Module7 ==--
Public Enum EnumTypeName
    EnumItemALICE
    EnumItemBOB
End Enum

Parte 2:¿Cómo llegamos aquí?

Nunca he hablado con nadie del equipo de desarrollo interno de VBA. Nunca he visto ninguna documentación oficial sobre por qué el IDE de VBA funciona de la forma en que lo hace. Entonces, lo que voy a escribir es pura conjetura, pero creo que tiene sentido.

Durante mucho tiempo, me pregunté por qué en el mundo el IDE de VBA tendría este comportamiento. Después de todo, es claramente intencional. Lo más fácil para el IDE sería... nada. Si el usuario declara una variable en mayúsculas, déjela en mayúsculas. Si el usuario luego hace referencia a esa variable en minúsculas unas pocas líneas más adelante, deje esa referencia en minúsculas y la declaración original en mayúsculas.

Esta sería una implementación perfectamente aceptable del lenguaje VBA. Después de todo, el lenguaje en sí no distingue entre mayúsculas y minúsculas. Entonces, ¿por qué tomarse la molestia de cambiar automáticamente las mayúsculas y minúsculas del identificador?

Irónicamente, creo que la motivación era evitar confusiones. (Golpe y fallo, si me preguntas).  Me burlo de esta explicación, pero tiene cierto sentido.

Contraste con idiomas sensibles a mayúsculas y minúsculas

Primero, hablemos de los programadores que provienen de un lenguaje sensible a mayúsculas y minúsculas. Una convención común en los lenguajes que distinguen entre mayúsculas y minúsculas, como C#, es nombrar objetos de clase con letras mayúsculas y nombrar instancias de esos objetos con el mismo nombre que la clase, pero con una letra minúscula inicial.

Esa convención no funcionará en VBA, porque dos identificadores que difieren solo en mayúsculas y minúsculas se consideran equivalentes. De hecho, el IDE de VBA de Office no le permitirá declarar simultáneamente una función con un tipo de mayúsculas y minúsculas y una variable local con un tipo diferente de mayúsculas y minúsculas (cubrimos esto exhaustivamente más arriba). Esto evita que el desarrollador asuma que existe una diferencia semántica entre dos identificadores con las mismas letras pero con mayúsculas y minúsculas diferentes.

Hacer que el código incorrecto parezca incorrecto

La explicación más probable en mi mente es que esta "característica" existe para hacer que los identificadores equivalentes se vean idénticos. Piénsalo; sin esta característica, sería fácil que los errores tipográficos se convirtieran en errores de tiempo de ejecución. ¿No me crees? Considere esto:

Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"

Private Sub Class_Initialize()
    mAccountName = ACCOUNT_NAME
End Sub

Public Property Get MyAccountName() As String
    MAccountName = Account_Name
End Property

Public Property Let MyAccountName(AccountName As String)
    mAccountName = Account_Name
End Property

Si echa un vistazo rápido al código anterior, parece bastante sencillo. Es una clase con .MyAccountName propiedad. La variable miembro de la propiedad se inicializa en un valor constante cuando se crea el objeto. Al configurar el nombre de la cuenta en el código, la variable miembro se actualiza nuevamente. Al recuperar el valor de la propiedad, el código simplemente devuelve el contenido de la variable miembro.

Al menos, eso es lo que se supone que debe hacer. Si copio el código anterior y lo pego en una ventana IDE de VBA, la carcasa de los identificadores se vuelve consistente y los errores de tiempo de ejecución aparecen repentinamente:

Private mAccountName As String
Private Const ACCOUNT_NAME As String = "New User"

Private Sub Class_Initialize()
    mAccountName = ACCOUNT_NAME   ' <- This is OK
End Sub

Public Property Get MyAccountName() As String
    mAccountName = ACCOUNT_NAME   ' <- This is probably not what we intended
End Property

Public Property Let MyAccountName(AccountName As String)
    mAccountName = ACCOUNT_NAME   ' <- This is definitely not what we meant
End Property

Implementación:¿Es este realmente el mejor enfoque?

Umm no. No me malinterpretes. De hecho, me gusta mucho la idea de cambiar automáticamente las mayúsculas de los identificadores para mantener la coherencia. Mi única queja real es que el cambio se realiza en cada identificador con ese nombre en todo el proyecto. Mucho mejor sería cambiar el uso de mayúsculas solo de aquellos identificadores que se refieren a la misma "cosa" (ya sea que esa "cosa" sea una función, sub, propiedad, variable, etc.).

Entonces, ¿por qué no funciona de esta manera? Espero que los desarrolladores de VBA IDE estén de acuerdo con mi perspectiva sobre cómo debería funcionar. Pero hay una muy buena razón por qué el IDE no funciona de esa manera. En una palabra, rendimiento.

Desafortunadamente, solo hay una forma confiable de descubrir qué identificadores con el mismo nombre realmente se refieren a lo mismo:analizar cada línea de código. Eso es lentoooowwww. Esto es más que una simple hipótesis de mi parte. El proyecto Rubberduck VBA en realidad hace exactamente esto; analiza cada línea de código en el proyecto para que pueda realizar un análisis de código automatizado y un montón de otras cosas interesantes.

El proyecto es ciertamente pesado. Probablemente funcione muy bien para proyectos de Excel. Desafortunadamente, nunca he sido lo suficientemente paciente como para usarlo en ninguno de mis proyectos de Access. Rubberduck VBA es un proyecto técnicamente impresionante, pero también es una advertencia. Respetar el alcance al cambiar el uso de mayúsculas para los identificadores sería bueno, pero no a expensas del actual rendimiento increíblemente rápido de VBA IDE.

Pensamientos finales

Entiendo la motivación de esta función. Creo que incluso entiendo por qué se implementa de la forma en que se hace. Pero es la peculiaridad más enloquecedora de VBA para mí.

Si pudiera hacer una sola recomendación al equipo de desarrollo de Office VBA, sería ofrecer una configuración en el IDE para deshabilitar los cambios automáticos de casos. El comportamiento actual podría permanecer habilitado de forma predeterminada. Pero, para los usuarios avanzados que intentan integrarse con los sistemas de control de versiones, el comportamiento podría desactivarse por completo para evitar que los "cambios de código" molestos contaminen el historial de revisiones.