ByVal是骗人的吗?

时间:2018-04-15 18:28:25

标签: excel vba pass-by-reference pass-by-value

我想我最近(最后)开始了解ByValByRef在VBA中的工作方式。

观察

在我迄今为止所做的所有教程和参考文献中,ByRef总是“传递对象”,ByVal是“传递对象的副本”。对我来说,后者意味着对象在内存中的位置是重复的,并返回指向该新位置的指针。我现在意识到情况并非总是如此,事实上,据我所知,情况很少:相反,即使在例程的签名中指定了ByRef,大多数对象和类实际上都会传递ByVal

静默地传递System.Collections.ArrayList ByRef,如以下代码所示:

Sub test()
    Dim list1 As Object, list2 As Object
    Set list1 = CreateObject("System.Collections.ArrayList")
    list1.Add "foo"
    Set list2 = RemoveItem(list1)
    Debug.Assert list2.Contains("foo") = False 'as expected
    Debug.Assert list1.Contains("foo") = True  'raises error, meaning list1 was passed byref not byval
End Sub

Function RemoveItem(ByVal list As Object) As Object 'ByVal
    list.Remove "foo" 'expect to remove from a copy and return that
    Set RemoveItem = list
End Function

鉴于我认为我对ByVal的了解,这让我感到惊讶。进一步的挖掘告诉我,要从ByVal获得我想要的副本,我需要我传递的对象有一个方法来启用它。对于ArrayList.Clone方法会生成浅层副本。所以我的功能变成了:

Function RemoveItem(ByRef list As Object) As Object 'ByRef or ByVal, makes no difference
    Dim listCopy As Object
    Set listCopy = list.Clone 'make a shallow copy of the object
    listCopy.Remove "foo" 'actually remove from a copy and return that
    Set RemoveItem = listCopy
End Function

VB Array在传递ByVal时引发编译器错误,可能是对此的警告

问题

所有这些让我思考:

  1. 类别/对象的ByValByRef之间有什么区别(如果有的话)
  2. 某些类型(BooleanLong)的区别是哪些类型可以从不能
  3. 的类和类型传递ByVal
  4. 就图书馆参考而言 - 可能无法用VB6写的东西
    • 是否可以创建具有默认ByVal响应的对象?
      • 在Python中,您可以指定类对不同关键字的反应。我怀疑你可以在VBA中原生地做到这一点,但是用一种更通用的语言写的对象可以说,检测它是否被传递ByVal并返回自己的.Clone
      • 换句话说,VBA的内置数据类型如何传递ByVal,并且任何其他对象都可以模仿这种行为(同样,在Python中,所有东西都是一个对象,因此可以复制所有行为*)。或者我不必担心这种事情会发生吗?
    • 是否有可能模仿Array的行为并在传递ByVal时引发编译器错误 - 因为数组没有克隆方法且无法轻易复制,所以总是ByRef
  5. 是否有Sub,Function或方法可以让我创建对象的深层拷贝?也就是说,复制一个对象使用的内存,并创建一个对象的第二个副本的通用方法。
  6. * 轶事,我是Python的新手

2 个答案:

答案 0 :(得分:3)

  

在我迄今为止所有的教程和参考文献中,ByRef总是&#34;传递对象&#34;,ByVal是&#34;传递对象的副本&#34;。< / p>

您是否有上述示例的链接?

  1. 对于(从类创建)的对象fragmentFragment类似,而ByVal*foo类似。

  2. 布尔值和长度是基元,对于基元ByRef类似于**foo,而ByVal类似于bar

      • 对象无法判断它是否已通过ByRef*bar
      • VBA&#39;内置&#39;数据类型在C / C ++中有类似物,因此ByRef是一个32位整数,实际上是ByVal,但只能取两个值中的一个(0 = False,-1 = True)。你不用担心,VBA会以安全的名义限制。
    • 如果要强制为对象传递Long,请创建一个Type而不是一个类。
  3. 对于类,您必须编写自己的浅层和深层复制构造函数。但只能使用浅层和深层Boolean来复制类型。

  4. 数组和类型传递ByRef,因为它们是在堆栈上创建的。

答案 1 :(得分:1)

对于VBA中的对象和类,传递byval或byref没有实际区别。传递byref ony意味着指针被传递&#39; byref&#39;,对象保持不变。

如果你想到它,它对(Excel)对象有意义;你是否通过 范围(&#34; A1&#34;)byref或byval,它仍然是我们正在寻址的同一个单元格。