Je fais face à quelque chose de bizarre dans VBScript. Lors de l'écriture d'une procédure où je veux que le paramètre soit passé par référence, la façon d'appeler cette procédure change la façon dont le paramètre est passé!
Voici un exemple :
Sub IncrementByRef(ByRef Value)
Value = Value + 1
End Sub
Sub IncrementByVal(ByVal Value)
Value = Value + 1
End Sub
Dim Num
Num = 10
WScript.Echo "Num : " & Num
IncrementByRef(Num) : WScript.Echo "IncrementByRef(Num) : " & Num
IncrementByRef Num : WScript.Echo "IncrementByRef Num : " & Num
IncrementByVal(Num) : WScript.Echo "IncrementByVal(Num) : " & Num
IncrementByVal Num : WScript.Echo "IncrementByVal Num : " & Num
Et voici la sortie:
U:\>cscript //nologo byrefbyval.vbs
Num : 10
IncrementByRef(Num) : 10
IncrementByRef Num : 11
IncrementByVal(Num) : 11
IncrementByVal Num : 11
U:\>
Lorsque vous spécifiez que les paramètres sont passés ByVal, cela fonctionne comme prévu, peu importe la façon dont la procédure est appelée. Mais lorsque vous spécifiez les paramètres passés ByRef, cela fonctionnera comme prévu si vous appelez la procédure de cette façon:
IncrementByRef Num
mais pas de cette façon:
IncrementByRef(Num)
Cela me semble bizarre. Existe-t-il un moyen de s'assurer que les paramètres sont passés ByRef, peu importe comment la procédure est appelée?
Eric Lippert a un excellent article sur l'utilisation des parenthèses dans VBScript: Que voulez-vous dire "ne peut pas utiliser de parenthèses?" Votre exemple illustre l'un des points qu'il mentionne, à savoir: L'argument ByRef
entre parenthèses le passe comme ByVal
.
En bref, les parenthèses dans les appels de sous-programme VBScript peuvent être placées non seulement autour de la liste des arguments, mais aussi autour des arguments individuels (auquel cas ils sont forcés ByVal
). Et VBScript s'attend uniquement à ce que la liste d'arguments soit placée entre parenthèses si le mot clé Call
est utilisé. Étant donné que l'appel IncrementByRef(Num)
n'utilise pas le mot clé Call
, VBScript traite les parenthèses comme appliquées à l'argument de sous-routine et lui transmet donc ByVal
au lieu de ByRef
.
Déroutant, mais c'est ainsi que cela fonctionne.
C'est une fonctionnalité, pas un bug:
http://msdn.Microsoft.com/en-us/library/ee478101.aspx
Un paramètre ByRef est transmis par valeur si l'argument est placé entre parenthèses et que les parenthèses ne s'appliquent pas à la liste d'arguments.
Les parenthèses s'appliquent à la liste d'arguments si l'une des conditions suivantes est vraie:
L'instruction est un appel de fonction qui a une affectation à la valeur renvoyée.
L'instruction utilise le mot clé Call. (Le mot clé Call peut éventuellement être utilisé pour un appel de sous-programme ou pour un appel de fonction sans affectation.)
Essayez donc d'utiliser le mot clé Call ou de lui renvoyer une valeur.
Pour être clair. Les parenthèses ont trois objectifs différents.
Il existe deux façons d'appeler une procédure soit sous la forme d'une instruction, soit sous la forme d'une expression.
Expression:-
x = func(y)
Déclaration:-
func y
Notez que le mot clé Call
appelle la procédure comme si elle faisait partie d'une expression, la liste d'arguments doit donc être contenue entre parenthèses.
Dans ce qui précède, y
lui-même représente une expression très simple. Nous aurions bien pu utiliser y + z
À ce point. En fait, nous pouvons utiliser n'importe quelle expression valide à ce stade, y compris celle qui utilise l'opérateur entre parenthèses. Par exemple:-
x = (y)
est une expression valide. Par conséquent, lorsque vous faites: -
func(y)
VBScript voit l'appel à func
auquel le résultat de l'expression (y)
est passé. Maintenant, même si func
définit ce paramètre comme ByRef
, la valeur dans y
ne sera pas affectée car y
n'est pas réellement passée en tant que paramètre. Ce qui a été adopté est le résultat de l'expression (y)
qui serait stocké quelque part temporairement. Même si ce magasin temporaire est modifié par func
, il serait ignoré par la suite et a donc le même comportement si le paramètre avait été marqué ByVal
.
IncrementByRef Num
appels et incréments à l'aide d'une référence à Num
IncrementByRef (47 + 3)
appels et incréments en utilisant une référence à "50". Qui est jeté à la sortie.
IncrementByRef (Num)
IncrementByRef (Num + 18)*5
IncrementByRef Cint("32")
Sont tous légaux en BASIC, tout comme en FORTRAN. Notoirement, dans un des premiers FORTRAN, passer par ref vous a permis de changer la valeur d'expressions comme
5
Ce qui était suffisamment déroutant pour que seuls les très petits premiers compilateurs FORTRAN aient ce genre de comportement.
Je ne suis pas sûr de suivre la question ou les réponses, mais je vais partager cela.
Que vous ayez une sous-routine de fonction, la définition des paramètres passés dans ByVal
ou ByRef
déterminera si la valeur du paramètre conserve sa valeur en dehors de l'appel de sous-routine ou de fonction. Si j'ai la fonction suivante:
Function ThisFByRef(ByRef MyValue)
End Function
Tout ce que je fais au paramètre dans la fonction (ou sous-routine) conservera sa valeur une fois la fonction terminée. ByVal
et ByRef
sont associés à la portée de la sous-routine ou de la fonction. Tout paramètre passé ByVal
ne conservera pas les modifications qui se produisent dans la sous-routine ou la fonction appelée. Alternativement, tout paramètre passé ByRef
conservera la valeur à laquelle il a été modifié dans la sous-routine ou la fonction. Le renvoi d'une valeur ne peut être effectué qu'avec une fonction et non une sous-routine et n'affecte pas la valeur du paramètre transmis, sauf si le paramètre est passé ByRef
et modifié dans la fonction. Par exemple:
Dim x
Dim ThisFValue
x = 0
ThisFValue = ThisFByRef(x)
At this point the values would be:
ThisFValue = 2
x = 1
x = 0
ThisFValue = ThisFByVal(x)
At this point the values would be:
ThisFValue = 2
x = 0
Function ThisFByRef(ByRef x)
x = x + 1
ThisFByRef = x + 1
End Function
Function ThisFByVal(ByVal x)
x = x + 1
ThisFByVal = x + 1
End Function