VBScript中的ByRef和ByVal

VBScript中的ByRef和ByVal,vbscript,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 Increment

我在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传递时,无论调用过程的方式如何,它都会按预期工作。 但是,当指定参数时,如果以这种方式调用过程,它将按预期工作:

IncrementByRef Num
IncrementByRef(Num)
但不是这样:

IncrementByRef Num
IncrementByRef(Num)

我觉得这很奇怪。有没有办法确保参数是通过ref传递的,不管过程是如何调用的?

这是一个特性,而不是一个bug:

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

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

  • 该语句是对返回值进行赋值的函数调用

  • 语句使用Call关键字。(Call关键字可选择性地用于子例程调用,或用于无赋值的函数调用。)

因此,请尝试使用Call关键字或让它返回值。

有一篇关于在VBScript中使用括号的文章: 您的示例说明了他提到的一点,即:将
ByRef
参数括在括号中,将其作为
ByVal
传递

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


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

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

  • 用于在定义或调用过程时包含参数列表
  • 指示数组上的索引器
  • 作为表达式中的运算符
  • 有两种方法可以将过程作为语句或表达式调用

    表达方式:-

    x = func(y)
    
    声明:-

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

    在上文中,
    y
    本身代表了一个非常简单的支出。在这一点上,我们完全可以使用
    y+z
    。事实上,此时我们可以使用任何有效的表达式,包括使用圆括号运算符的表达式。例如:-

     x = (y)
    
    是一个有效的表达式。因此,当您这样做时:-

     func(y)
    
    VBScript将看到对
    func
    的调用,表达式
    (y)
    的结果将传递给该调用。现在,即使
    func
    将此参数定义为
    ByRef
    y
    中的值不会受到影响,因为
    y
    实际上没有作为参数传递。传递的是表达式
    (y)
    的结果,该表达式将存储在临时位置。即使此临时存储被
    func
    修改,它也会在以后被丢弃,因此,如果参数被标记为
    ByVal
    ,它的行为也会相同

    IncrementByRef Num
    
    使用对Num的引用调用和递增

    IncrementByRef (47 + 3)
    
    调用并使用对“50”的引用递增。在出口处被丢弃

    IncrementByRef (Num)
    IncrementByRef (Num + 18)*5
    IncrementByRef Cint("32")
    
    在BASIC中都是合法的,就像在FORTRAN中一样。众所周知,在早期的FORTRAN中,通过ref传递可以更改表达式的值,如

    5
    

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

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

    对于无返回值:

    myFunction "This is a reference"
    
    myValue = myFunction ("This is a reference")
    
    对于返回值:

    myFunction "This is a reference"
    
    myValue = myFunction ("This is a reference")
    

    我不确定我是否遵循了问题或答案,但我将分享这一点

    无论您是否有函数的子程序,定义传入
    ByVal
    ByRef
    的参数将确定参数值是否在子程序或函数调用之外保留其值。如果我有以下功能:

    Function ThisFByRef(ByRef MyValue)
    End Function
    
    我在函数(或子程序)中对参数所做的任何操作都将在函数完成后保留其值
    ByVal
    ByRef
    与子程序或函数的范围相关联。通过val传递的任何参数都不会保留被调用子程序或函数中发生的更改。或者,通过ref
    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
    

    这是我见过的与VBScript相关的最令人困惑的MSDN主题之一…这么简单?我挣扎了一个小时!谢谢,+1。很多旧的MSDN内容已经被微软储存起来了。Eric Lippert引用的文章仍然可以在互联网档案中找到:@mnemotronic也可以在Eric的博客中找到。我已经更新了答案中的链接。IMHO,你的答案没有阐明我的观点,我觉得奇怪的是,在调用
    ThisFValue=ThisByRef(x)
    后,x包含1,但如果我这样调用它,则x包含0。相同的函数,相同的参数,但结果不同!