2011-03-24 11 views
27

Acabo de tener unos irritantes 30 minutos en un "error de compilación" en VBA (Access 2003) causado por el uso de corchetes alrededor de los argumentos que estoy pasando a un Sub I definido.¿Cuáles son las reglas que rigen el uso de corchetes en las llamadas a funciones de VBA?

He estado buscando un artículo/tutorial/instrucción decente sobre cuándo son necesarios/apropiados/inadecuados/prohibidos los paréntesis, pero no encuentro ninguna guía clara.

+2

Aquí mi publicación favorita sobre este tema: http://dailydoseofexcel.com/archives/2012/05/01/quick-vba-tip-parentheses/ –

Respuesta

21

De Here:

Uso de la instrucción VBScript llamada a llamar a una subrutina El uso de la instrucción Call es opcional cuando se desea llamar a una subrutina. El propósito de la declaración de llamada cuando se usa con un Sub es permitirle encerrar la lista de argumentos entre paréntesis. Sin embargo, si una subrutina no pasa ningún argumento, entonces aún no debe usar paréntesis cuando llama a un Sub utilizando la instrucción Call.

Call MySubroutine 

Si una subrutina tiene argumentos, debe usar paréntesis cuando usa la instrucción Call. Si hay más de un argumento, debe separar los argumentos con comas.

Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru") 

Llamada de la función Hay dos maneras posibles para llamar a una función. Puede llamar a la función directamente, solo por nombre, o puede llamarla utilizando la declaración de VBScript Call.

Llamar a una función por Nombre Al llamar a una función directamente por su nombre y cuando no hay una asignación a un valor devuelto, todos de los siguientes son sintaxis legal:

MyFunction 
MyFunction() 
MyFunction intUsageFee, intTimeInHours, "DevGuru" 

Si quieres un Volverá valor, puede asignar la función a una variable. Tenga en cuenta que si hay uno o más argumentos, debe usar los paréntesis.

returnval = MyFunction 
returnval = MyFunction() 
returnval = MyFunction(intUsageFee, intTimeInHours, "DevGuru") 
+7

Gracias - parece que mi problema fue porque mi función no está devolviendo un valor, pero Todavía estaba usando corchetes alrededor de mi lista de argumentos.Esto parece una decisión de sintaxis bastante extraña ... – HorusKol

3

Cuando se utiliza Call MySub debería usar paréntesis alrededor de los parámetros, pero si se omite de llamadas, que no necesita paréntesis.

5

yo pasamos 10 minutos averiguar un "tipos incompatibles" excepción al llamar a una Sub que tiene 1 argumento a través de

CallMe(argument) 

Como resultado, esto no es válido, buscando en Google me llevó aquí y finalmente

Call CallMe(argument) 

o

CallMe argument 

hizo el truco. Por lo tanto, no debe usar los corchetes cuando llama a un sub sin el enunciado de llamada que solo toma 1 argumento.

+0

+ _1 para el Subnombre –

7

Acabo de encontrar un comportamiento raro llamando a una función con/sin paréntesis. Google me llevó hasta aquí.

sub test() 
    dim a as double 
    a = 1# 
    p(a) 'this won't change a's value 
    Debug.Print a '1 
    p a ' this is expected behavior 
    Debug.Print a '2 
    Call p(a) 'this is also valid 
    Debug.Print a '3 
end sub 

Function p(a as Double) 'default is byref 
    a = a + 1 
end function 

Mi conclusión es que usted tiene que utilizar cualquiera de las llamadas u omitir los paréntesis cuando se llama a una función con un solo parámetro, de lo contrario el parámetro no se pasa por referencia (que sigue siendo ser llamado, como ya lo comprobé) .

+0

El paréntesis sí obliga a pasar un argumento 'ByVal'. – RubberDuck

44

Existe una lógica perfecta para la regla de paréntesis en VB (A), y así es.

Si se llama a un procedimiento (función o sub) con argumentos, y la llamada se alinea con otras declaraciones o palabras clave, los argumentos deben estar entre paréntesis. Esto para distinguir los argumentos que pertenecen a la llamada de procedimiento del resto de la línea. Entonces:

1: If CheckConditions(A, B, C) = DONT_PROCEED Then Exit Sub 

es una línea válida; la llamada a CheckConditions necesita los paréntesis para indicar qué otros bits de la línea son sus argumentos. Por el contrario, esto produciría un error de sintaxis:

2: If CheckConditions A, B, C = DONT_PROCEED Then Exit Sub 

Porque es imposible de analizar.

Con una llamada de procedimiento como la única declaración en la línea, no se necesitan paréntesis, porque está claro que los argumentos pertenecen a la llamada de procedimiento:

3: SaveNewValues Value1, Value2, Value3 

Si bien esto resulta en un error de sintaxis (por sólidas razones discutidas más adelante):

4: SaveNewValues(Value1, Value2, Value3) 

para evitar confusiones entre paréntesis o sin paréntesis (de hecho, para evitar la Regla paréntesis en su totalidad), siempre es una buena idea utilizar la palabra clave de llamada para las llamadas de este tipo; que asegura que la llamada a procedimiento no es la única declaración en la línea, paréntesis, por lo que requiere:

5: Call SaveNewValues(Value1, Value2, Value3) 

Así que si usted consigue en el hábito de la anterior procedimiento autónomo de llamadas con la palabra clave Call, puede olvidarse de los paréntesis Regla, porque entonces siempre puede incluir sus argumentos entre paréntesis.

El asunto se confunde con el papel adicional que juegan los paréntesis en VB (A) (y en muchos otros idiomas): también indican la prioridad de evaluación para las expresiones. Si usa paréntesis en cualquier otro contexto pero para encerrar los argumentos de llamada de procedimiento, VB (A) intentará evaluar la expresión entre paréntesis en un valor simple resultante.

Por lo tanto, en el ejemplo 4, donde los paréntesis son ilegales para adjuntar los argumentos, VB (A) intentará en su lugar evaluar la expresión entre paréntesis. Dado que (Valor1, Valor 2, Valor3) no es una expresión que se puede evaluar, se produce un error de sintaxis.

Esto también explica por qué las llamadas con una variable pasada ByRef actúan como si se llamaran ByVal si el argumento está entre paréntesis. En el ejemplo anterior, donde la función p es llamado con ByRef parámetro a, hay una gran diferencia entre estas dos llamadas a p:

6: p a 

Y

Como se discutió anteriormente, 6 es la correcta sintaxis: la llamada está sola en su línea, por lo tanto, los paréntesis no deben usarse para encerrar los argumentos.

En 7, el argumento está entre paréntesis de todos modos, lo que hace que VB (A) evalúe la expresión adjunta en un valor simple. Que, por supuesto, es la definición misma de pasar ByVal. Los paréntesis aseguran que en lugar de un puntero a a, se pasa el valor de a, y a no se modifica.

Esto también explica por qué la regla de paréntesis no siempre parece mantener el dominio. ejemplo más claro es una llamada MsgBox:

8: MsgBox "Hello World!" 

Y

9: MsgBox ("Hello World!") 

son ambos correctos, aunque la regla paréntesis dicta que 9 deben estar equivocado. Lo es, por supuesto, pero todo lo que sucede es que VB (A) evalúa la expresión entre paréntesis. Y el literal de cadena evalúa exactamente el mismo literal de cadena, de modo que la llamada real realizada es 8. En otras palabras: las llamadas a procedimientos de argumento único con argumentos constantes o de cadena literal tienen el mismo resultado con o sin paréntesis. (Esta es la razón por la cual incluso mis llamadas a MsgBox están precedidas por la palabra clave de llamada.)

Finalmente, esto explica los errores de No coinciden los tipos y el comportamiento extraño al pasar argumentos de objeto. Digamos que su aplicación tiene un procedimiento HighlightContent que toma un TextBox como argumento (y, nunca lo adivinará, lo resalta). Usted llama a esto para seleccionar todo el texto en el cuadro de texto. Puede llamar a este procedimiento de tres formas sintácticamente correctas:

10: HighlightContent txtName 
11: HighlightContent (txtName) 
12: Call HighlightContent(txtName) 

digamos que su usuario ha entrado en "John" en el cuadro de texto y la aplicación llama HighlightContent. ¿Qué sucederá, qué llamada funcionará?

10 y 12 son correctos; el nombre John se resaltará en el cuadro de texto. Pero 11 es sintácticamente correcto, pero dará como resultado un error de compilación o de tiempo de ejecución. ¿Por qué? Porque los paréntesis están fuera de lugar. Esto provocará que VB (A) intente una evaluación de la expresión entre paréntesis. Y el resultado de la evaluación de un objeto generalmente será el valor de su propiedad predeterminada; .Texto, en este caso. Así que llamar al procedimiento como 11 no pasará el objeto TextBox al procedimiento, sino un valor de cadena "John". Resultando en una Falta de coincidencia de tipo.

+5

+1 para obtener una respuesta excelente, pero sigo sin estar de acuerdo con que la regla de paréntesis sea "perfectamente lógica" ... ¡No puedo imaginar una manera más torpe de manejar algo simple como paréntesis! – Emma

+1

¿Qué pasa cuando hay 'puntos'? (no dude en corregir mi terminología) 'myCollection.add obj' AND ' myCollection.item (obj) ' ¿No son ambas las formas correctas de hacer esto? Pero las reglas de paréntesis son diferentes, y no sé por qué. – bmende

+0

Respuesta completa a algo que me ha intrigado por un tiempo. Todavía parece un poco tonto. Otros lenguajes no tienen problemas para analizar llamadas de función con paréntesis y sin palabra clave "call". Pero ahora que conozco las reglas, ¡no perderé el tiempo tratando de descubrir WTFITMWTSL! Gracias por la ayuda. B^J – riderBill

2

1 - Por defecto, no use paréntesis al llamar a procedimientos o funciones:

MsgBox "Hello World" 

2 - Si se llama a una función, y están interesados ​​en su resultado, a continuación, se debe incluir sus argumentos con paréntesis :

Dim s As String 
Dim l As Long 
s = "Hello World" 
l = Len(s) 

3 - Si desea utilizar la palabra clave llamada con un procedimiento, a continuación, debe incluir las discusiones con paréntesis (por ejemplo, cuando se quiere asignar el resultado en una variable o utilizar la función en una expresión):

Call MsgBox("Hello World") 

4 - Si desea forzar un argumento ByRef (por defecto) para pasar ByVal, a continuación, adjuntar el argumento ByRef con paréntesis:

Sub Test 
    Dim text As String 
    text = "Hello World" 

    ChangeArgument((text)) 

    MsgBox text 
End Sub 

Sub ChangeArgument(ByRef s As String) 
    s = "Changed" 
End Sub 

Esto muestra "Hello World"

Cuestiones relacionadas