Quali sono le regole che regolano l’uso delle parentesi nelle chiamate alle funzioni VBA?

Ho appena avuto un irritante 30 minuti su un “errore del compilatore” in VBA (Access 2003) causato dal mio uso di parentesi attorno agli argomenti che sto passando ad un Sub che ho definito.

Ho cercato un articolo / tutorial / istruzione decente su quando le parentesi sono necessarie / appropriate / inappropriate / vietate, ma non riesco a trovare linee guida chiare.

Da qui :

Utilizzo dell’istruzione di chiamata VBScript per chiamare una subroutine L’uso dell’istruzione Call è facoltativo quando si desidera chiamare una subroutine. Lo scopo dell’istruzione Call quando utilizzato con un Sub è quello di consentire di racchiudere l’elenco degli argomenti tra parentesi. Tuttavia, se una subroutine non passa alcun argomento, allora non si dovrebbero usare le parentesi quando si chiama un Sub usando l’istruzione Call.

Call MySubroutine 

Se una subroutine ha argomenti, è necessario utilizzare le parentesi quando si utilizza l’istruzione Call. Se è presente più di un argomento, è necessario separare gli argomenti con virgole.

 Call MySubroutine(intUsageFee, intTimeInHours, "DevGuru") 

Chiamata alla funzione Ci sono due modi possibili per chiamare una funzione. Puoi chiamare la funzione direttamente, solo per nome, o puoi chiamarla usando l’istruzione VBScript Call.

Chiamata di una funzione per nome Quando si chiama una funzione direttamente per nome e quando non vi è alcuna assegnazione a un valore restituito, tutte le seguenti sono syntax legale:

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

Se si desidera un valore restituito, è ansible assegnare la funzione a una variabile. Si noti che se sono presenti uno o più argomenti, è necessario utilizzare le parentesi.

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

C’è una logica perfetta per la regola Parentheses in VB (A), e va così.

Se una procedura (funzione o sub) viene chiamata con argomenti e la chiamata si trova su una riga con altre istruzioni o parole chiave, gli argomenti devono essere racchiusi tra parentesi. Questo per distinguere gli argomenti appartenenti alla chiamata di procedura dal resto della riga. Così:

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

è una linea valida; la chiamata a CheckConditions necessita delle parentesi per indicare quali altri bit della linea sono i suoi argomenti. Viceversa, ciò produrrebbe un errore di syntax:

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

Perché è imansible da analizzare.

Con una chiamata di procedura come unica istruzione sulla riga, le parentesi non sono necessarie perché è chiaro che gli argomenti appartengono alla chiamata della procedura:

 3: SaveNewValues Value1, Value2, Value3 

Mentre questo si traduce in un errore di syntax (per ragioni valide discusse di seguito):

 4: SaveNewValues(Value1, Value2, Value3) 

Per evitare confusione tra parentesi o nessuna parentesi (infatti, per evitare completamente la regola Parentheses), è sempre una buona idea usare la parola chiave Call per le chiamate come queste; ciò garantisce che la chiamata alla procedura non sia l’unica affermazione sulla linea, quindi richiede parentesi:

 5: Call SaveNewValues(Value1, Value2, Value3) 

Quindi, se hai l’abitudine di precedere le chiamate di procedura autonoma con la parola chiave Chiama, puoi dimenticare la regola delle parentesi, perché puoi quindi sempre racchiudere i tuoi argomenti tra parentesi.

La questione è confusa dal ruolo aggiuntivo che le parentesi riproducono in VB (A) (e in molte altre lingue): indicano anche la precedenza di valutazione per le espressioni. Se si utilizzano le parentesi in qualsiasi altro contesto ma per includere gli argomenti di chiamata della procedura, VB (A) tenterà di valutare l’espressione tra parentesi con un valore semplice risultante.

Quindi, nell’esempio 4, dove le parentesi sono illegali per racchiudere gli argomenti, VB (A) tenterà invece di valutare l’espressione tra parentesi. Poiché (Valore1, Valore 2, Valore3) non è un’espressione che può essere valutata, ne consegue un errore di syntax.

Questo spiega anche perché le chiamate con una variabile passata da ByRef agiscono come se fossero chiamate ByVal se l’argomento è racchiuso tra parentesi. Nell’esempio sopra, dove la funzione p è chiamata con il parametro ByRef a, c’è una grande differenza tra queste due chiamate a p:

 6: pa 

E

 7: p(a) 

Come discusso sopra, 6 è la syntax corretta: la chiamata è sola sulla sua linea, quindi le parentesi non dovrebbero essere usate per racchiudere gli argomenti.

In 7, l’argomento è comunque racchiuso tra parentesi, suggerendo a VB (A) di valutare l’espressione racchiusa in un valore semplice. Che naturalmente è la definizione stessa di passare ByVal. Le parentesi assicurano che anziché un puntatore a, il valore di a viene passato e a non viene modificato.

Questo spiega anche perché la regola delle parentesi non sembra sempre avere influenza. L’esempio più chiaro è una chiamata MsgBox:

 8: MsgBox "Hello World!" 

E

 9: MsgBox ("Hello World!") 

Sono entrambi corretti, anche se la regola delle parentesi stabilisce che 9 dovrebbe essere sbagliato. È, ovviamente, ma tutto ciò che accade è che VB (A) valuta l’espressione tra parentesi. E la stringa letterale valuta esattamente la stessa stringa letterale, in modo che la chiamata effettiva effettuata sia 8. In altre parole: le chiamate a procedure a argomento singolo con argomenti letterali costanti o stringa hanno lo stesso risultato con o senza parentesi. (Questo è il motivo per cui anche le mie chiamate MsgBox sono precedute dalla parola chiave Call.)

Infine, questo spiega gli errori di tipo mancata corrispondenza di tipo e il comportamento strano durante il passaggio degli argomenti object. Supponiamo che la tua applicazione abbia una procedura HighlightContent che accetta un object TextBox come argomento (e, non indovinerai mai, evidenzia il contenuto). Si chiama per selezionare tutto il testo nella casella di testo. Puoi chiamare questa procedura in tre modi sintatticamente corretti:

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

Supponiamo che il tuo utente abbia inserito “John” nella casella di testo e che l’applicazione chiami HighlightContent. Cosa succederà, quale chiamata funzionerà?

10 e 12 sono corretti; il nome John sarà evidenziato nella casella di testo. Ma 11 è sintatticamente corretto, ma risulterà in un errore di compilazione o di runtime. Perché? Perché le parentesi sono fuori luogo. Ciò richiederà a VB (A) di tentare una valutazione dell’espressione tra parentesi. E il risultato della valutazione di un object sarà molto spesso il valore della sua proprietà predefinita; .Text, in questo caso. Quindi chiamare la procedura come 11 non passerà l’object TextBox alla procedura, ma un valore stringa “John”. Risultante in un errore di tipo.

Ho appena trovato un comportamento strano chiamando una funzione con / senza parentesi. Google mi ha portato qui.

 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 

La mia conclusione è che devi usare Call o omettere le parentesi quando si chiama una funzione con un solo parametro, altrimenti il ​​parametro non viene passato per riferimento (viene ancora chiamato, come ho già controllato).

Ho appena trascorso 10 minuti a capire un’eccezione di “tipi incompatibili” mentre chiamavo un Sub che accetta 1 argomento via

 CallMe(argument) 

A quanto pare, questo non è valido, googling mi portano qui e finalmente

 Call CallMe(argument) 

o

 CallMe argument 

ha fatto il trucco Quindi non devi usare le parentesi quando chiami un sottotitolo senza l’istruzione di chiamata che richiede solo 1 argomento.

Quando usi Call MySub devi usare parentesi intorno ai parametri, ma se ometti Call, non hai bisogno di parentesi.

1 – Per impostazione predefinita, non utilizzare le parentesi quando si chiamano procedure o funzioni:

 MsgBox "Hello World" 

2 – Se stai chiamando una funzione e sei interessato al suo risultato, devi racchiudere i suoi argomenti tra parentesi:

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

3 – Se si desidera utilizzare la parola chiave call con una procedura, è necessario racchiudere gli argomenti tra parentesi (ad es. Quando si desidera assegnare il risultato in una variabile o utilizzare la funzione in un’espressione):

 Call MsgBox("Hello World") 

4 – Se si desidera forzare un argomento ByRef (predefinito) per passare ByVal, quindi racchiudere l’argomento ByRef con parentesi:

 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 

Questo visualizza “Hello World”