VBScript中的ByRef和ByVal

时间:2009-10-08 13:33:18

标签: vbscript

我在VBScript中遇到了一些奇怪的事情。在编写我希望通过引用传递参数的过程时,调用此过程的方式会改变参数的传递方式!

以下是一个例子:

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

这是输出:

U:\>cscript //nologo byrefbyval.vbs
Num : 10
IncrementByRef(Num) : 10
IncrementByRef Num : 11
IncrementByVal(Num) : 11
IncrementByVal Num : 11

U:\>

当指定参数传递ByVal时,无论调用过程的方式如何,它都按预期工作。 但是当指定参数传递ByRef时,如果以这种方式调用过程,它将按预期工作:

IncrementByRef Num

但不是这样:

IncrementByRef(Num)

这对我来说似乎很奇怪。有没有办法确保参数是通过ByRef传递的,无论程序是如何调用的?

6 个答案:

答案 0 :(得分:40)

Eric Lippert有一篇关于在VBScript中使用括号的好文章:   What do you mean "cannot use parentheses?"您的示例说明了他提到的一点,即:在括号中括起ByRef参数将其作为ByVal传递。

简而言之,VBScript子例程调用中的括号不仅可以放在参数列表周围,还可以放在各个参数周围(在这种情况下,它们被强制ByVal)。如果使用Call关键字,VBScript只希望将参数列表括在括号中。由于IncrementByRef(Num)调用不使用Call关键字,因此VBScript将括号视为应用于子例程参数,因此将其传递给ByVal而不是ByRef

令人困惑,但这就是它的工作方式。

答案 1 :(得分:19)

这是一个功能,而不是一个错误:
http://msdn.microsoft.com/en-us/library/ee478101.aspx

  

如果参数括在括号中且括号不适用于参数列表,则按值传递ByRef参数。

     

如果满足下列条件之一,则括号将应用于参数列表:

     
      
  • 该语句是一个函数调用,具有对返回值的赋值。

  •   
  • 该语句使用Call关键字。 (Call关键字可以选择用于子程序调用,也可以用于没有赋值的函数调用。)

  •   

所以尝试使用Call关键字或让它返回一个值。

答案 2 :(得分:7)

要清楚。括号有三个不同的目的。

  1. 用于在定义或调用过程时附上参数列表
  2. 表示数组上的索引器。
  3. 作为表达式中的运算符。
  4. 有两种方法可以将过程作为语句或表达式来调用。

    表达式: -

    x = func(y)
    

    声明: -

    func y
    

    注意Call关键字调用过程就好像它是表达式的一部分,因此参数列表必须包含在parantheses中。

    在上文中y本身代表了一个非常简单的尝试。我们现在可以使用y + z。实际上我们可以使用任何有效的表达式,包括使用括号运算符的表达式。例如: -

     x = (y)
    

    是一个有效的表达式。因此当你这样做时: -

     func(y)
    

    VBScript看到func的调用,表达式(y)的结果传递给func。现在,即使ByRef将此参数定义为yy中的值也不会受到影响,因为(y)实际上并未作为参数传递。传递的是表达式func的结果,它将被临时存储在某处。即使这个临时存储被ByVal修改,它也会在之后被丢弃,因此如果参数被标记为{{1}},则具有相同的行为。

答案 3 :(得分:3)

IncrementByRef Num

使用对Num

的引用进行调用和增量
IncrementByRef (47 + 3)

使用对“50”的引用进行调用和增量。哪一个在退出时被丢弃。

IncrementByRef (Num)
IncrementByRef (Num + 18)*5
IncrementByRef Cint("32")

BASIC中的所有内容都是合法的,就像它们在FORTRAN中一样。众所周知,在一个早期的FORTRAN中,通过ref允许你改变像

这样的表达式的值
5

令人困惑的是,只有非常小的早期FORTRAN编译器才有这种行为。

答案 4 :(得分:0)

我不确定我是否遵循了这些问题,但我会分享这个问题。

无论您是否具有函数的子例程,定义在ByValByRef中传递的参数都将确定参数的值是否在子例程或函数调用之外保留其值。如果我有以下功能:

Function ThisFByRef(ByRef MyValue)
End Function

我对函数(或子例程)中的参数所做的任何操作都将在函数完成后保留其值。 ByValByRef与子例程或函数的范围相关联。传递的任何参数ByVal都不会保留在被调用的子例程或函数中发生的更改。或者,传递的任何参数ByRef将保留在子例程或函数中更改的值。返回值只能通过Function而不是Sub-Routine完成,并且不会影响传入的参数的值,除非参数在函数内传递ByRef并更改。例如:

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

答案 5 :(得分:-1)

很简单。创建函数或子函数时,可以使用以下方式调用它们:

没有返回值:

myFunction "This is a reference"

返回值:

myValue = myFunction ("This is a reference")