Quais são as regras que regem o uso de colchetes em chamadas de function do VBA?

Acabei de ter um irritante 30 minutos em um “erro do compilador” no VBA (Access 2003) causado pelo uso de colchetes em torno dos argumentos que estou passando para um Sub que eu defini.

Tenho procurado encontrar um artigo / tutorial / instrução decente sobre quando os colchetes são necessários / apropriados / inapropriados / proibidos, mas não consigo encontrar nenhuma orientação clara.

A partir daqui :

Usando a instrução de chamada do VBScript para chamar uma sub-rotina O uso da instrução de chamada é opcional quando você deseja chamar uma sub-rotina. A finalidade da instrução Call quando usada com um Sub é permitir que você inclua a lista de argumentos entre parênteses. No entanto, se uma sub-rotina não passar nenhum argumento, você ainda não deve usar parênteses ao chamar um Sub usando a instrução Call.

Call MySubroutine 

Se uma sub-rotina tiver argumentos, você deverá usar parênteses ao usar a instrução Call. Se houver mais de um argumento, você deve separar os argumentos com vírgulas.

 Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru") 

Chamando a function Existem duas maneiras possíveis de chamar uma function. Você pode chamar a function diretamente, apenas pelo nome, ou você pode chamá-lo usando a instrução VBScript Call.

Chamando uma function por nome Ao chamar uma function diretamente pelo nome e quando não há atribuição a um valor retornado, todas as seguintes são syntax legal:

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

Se você quiser um valor retornado, poderá atribuir a function a uma variável. Observe que, se houver um ou mais argumentos, você deverá usar os parênteses.

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

Existe uma lógica perfeita para a Regra de Parênteses em VB (A), e é assim.

Se um procedimento (function ou sub) for chamado com argumentos e a chamada estiver em uma linha com outras instruções ou palavras-chave, os argumentos deverão ser colocados entre parênteses. Isso para distinguir os argumentos pertencentes à chamada de procedimento do resto da linha. Assim:

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

é uma linha válida; A chamada para CheckConditions precisa dos parênteses para indicar que outros bits da linha são seus argumentos. Por outro lado, isso produziria um erro de syntax:

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

Porque é impossível analisar.

Com uma chamada de procedimento como a única instrução na linha, os parênteses não são necessários porque está claro que os argumentos pertencem à chamada de procedimento:

 3: SaveNewValues Value1, Value2, Value3 

Enquanto isso resulta em um erro de syntax (por motivos de som discutidos abaixo):

 4: SaveNewValues(Value1, Value2, Value3) 

Para evitar confusão sobre parênteses ou sem parênteses (na verdade, para evitar a Regra de Parênteses por completo), é sempre uma boa ideia usar a palavra-chave Call para chamadas como essas; Isso garante que a chamada de procedimento não seja a única declaração na linha, exigindo parênteses:

 5: Call SaveNewValues(Value1, Value2, Value3) 

Portanto, se você adquirir o hábito de preceder as chamadas de procedimento autônomo com a palavra-chave Call, poderá esquecer a Regra de Parênteses, pois você poderá colocar seus argumentos entre parênteses.

A questão é confusa pelo papel que os parênteses adicionais desempenham no VB (A) (e em muitas outras linguagens): eles também indicam precedência de avaliação para expressões. Se você usar parênteses em qualquer outro contexto, mas para include argumentos de chamada de procedimento, o VB (A) tentará avaliar a expressão entre parênteses para um valor simples resultante.

Assim, no exemplo 4, onde os parênteses são ilegais para include os argumentos, VB (A) tentará, em vez disso, avaliar a expressão entre parênteses. Como (valor1, valor2, valor3) não é uma expressão que pode ser avaliada, ocorre um erro de syntax.

Isso também explica porque chamadas com uma variável passada ByRef agir como se chamado ByVal se o argumento estiver entre parênteses. No exemplo acima, onde a function p é chamada com o parâmetro ByRef a, há uma grande diferença entre essas duas chamadas para p:

 6: pa 

E

 7: p(a) 

Como discutido acima, 6 é a syntax correta: a chamada está sozinha em sua linha, portanto, os parênteses não devem ser usados ​​para include os argumentos.

Em 7, o argumento é colocado entre parênteses, solicitando a VB (A) que avalie a expressão incluída em um valor simples. Qual, claro, é a própria definição de passar ByVal. Os parênteses garantem que, em vez de um ponteiro para a, o valor de a seja passado e a seja deixado inalterado.

Isso também explica por que a regra dos parênteses nem sempre parece prevalecer. O exemplo mais claro é uma chamada MsgBox:

 8: MsgBox "Hello World!" 

E

 9: MsgBox ("Hello World!") 

Ambos estão corretos, embora a regra dos parênteses determine que 9 deve estar errado. É, claro, mas tudo o que acontece é que VB (A) avalia a expressão entre parênteses. E o literal de string avalia exatamente o mesmo literal de string, de modo que a chamada real feita é 8. Em outras palavras: as chamadas para procedimentos de argumento único com argumentos literais de constante ou cadeia têm o mesmo resultado com ou sem parênteses. (É por isso que mesmo minhas chamadas MsgBox são precedidas pela palavra-chave Call.)

Por fim, isso explica erros de incompatibilidade de tipo ímpar e comportamento estranho ao passar argumentos do object. Digamos que seu aplicativo possua um procedimento HighlightContent que usa um TextBox como argumento (e, você nunca irá adivinhar, realça seu conteúdo). Você chama isso para selecionar todo o texto na checkbox de texto. Você pode chamar esse procedimento de três maneiras sintaticamente corretas:

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

Digamos que seu usuário tenha inserido “John” na checkbox de texto e seu aplicativo chame HighlightContent. O que vai acontecer, qual chamada vai funcionar?

10 e 12 estão corretos; o nome John será destacado na checkbox de texto. Mas 11 está sintaticamente correto, mas resultará em um erro de compilation ou de tempo de execução. Por quê? Porque os parênteses estão fora do lugar. Isso fará com que o VB (A) tente uma avaliação da expressão entre parênteses. E o resultado da avaliação de um object será, na maioria das vezes, o valor de sua propriedade padrão; .Texto, neste caso. Portanto, chamar o procedimento como 11 não passará o object TextBox para o procedimento, mas um valor de string “John”. Resultando em uma incompatibilidade de tipo.

Acabei de encontrar algum comportamento estranho chamando uma function com / sem parênteses. O Google me levou aqui.

 sub test() dim a as double a = 1# p(a) 'this won't change a's value Debug.Print a '1 pa ' 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 

Minha conclusão é que você tem que usar Call ou omitir os parênteses ao chamar uma function com apenas um parâmetro, caso contrário o parâmetro não é passado por referência (ele ainda é chamado, como eu já verifiquei).

Eu passei 10 minutos descobrindo uma exceção “incompatível com tipos” ao chamar um Sub que leva 1 argumento via

 CallMe(argument) 

Como se vê, isso é inválido, googling me levar aqui e finalmente

 Call CallMe(argument) 

ou

 CallMe argument 

fez o truque. Portanto, você não deve usar os colchetes ao chamar um sub sem a declaração de chamada, que leva apenas 1 argumento.

Quando você usa o Call MySub você deve usar parênteses em torno de parâmetros, mas se você omitir a chamada, você não precisa de parênteses.

1 – Por padrão, não use parênteses ao chamar procedimentos ou funções:

 MsgBox "Hello World" 

2 – Se você está chamando uma function, e está interessado em seu resultado, então você deve colocar seus argumentos entre parênteses:

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

3 – Se você quiser usar a palavra-chave call com um procedimento, então você deve colocar os argumentos entre parênteses (por exemplo, quando você quer atribuir o resultado em uma variável ou usar a function em uma expressão):

 Call MsgBox("Hello World") 

4 – Se você quiser forçar um argumento ByRef (o padrão) a ser passado ByVal, coloque o argumento ByRef entre parênteses:

 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 

Isso exibe “Hello World”