web-dev-qa-db-fra.com

ByRef et ByVal dans VBScript

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?

31
Jérôme

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.

40
Helen

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.

19
Joel Coehoorn

Pour être clair. Les parenthèses ont trois objectifs différents.

  1. Utilisé pour entourer une liste d'arguments lors de la définition ou de l'appel d'une procédure
  2. Pour indiquer un indexeur sur un tableau.
  3. En tant qu'opérateur dans une expression.

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.

7
AnthonyWJones
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.

3
david

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
0