ByVal 是个谎言吗?
Is ByVal a lie?
我想我最近(终于)开始了解 ByVal
和 ByRef
在 VBA 中的工作方式。
观察
在我迄今为止阅读的所有教程和参考资料中,ByRef
始终是 "passing the object",ByVal
始终是 "passing a copy of the object"。对我来说,后者意味着对象在内存中的位置被复制,指向新位置的指针被 returned。我现在意识到情况并非总是如此,事实上,据我所知,这种情况很少发生:相反,大多数对象和 classes 实际上都通过了 ByRef
,即使指定了 ByVal
在例程的签名中。
A 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
A VB Array
在传递 ByVal 时引发编译器错误,可能只是针对此
的警告
问题
所有让我想到的:
- 对于 classes/objects
,ByVal
和 ByRef
之间有什么区别(如果有)
- 区分可以传递
ByVal
的某些类型(Boolean
、Long
)与 class 和不能传递的类型
- 在图书馆参考方面 -
VB6
中可能没有写的东西
- 是否可以创建具有默认
ByVal
响应的对象?
- 在 Python 中,您可以指定 class 如何对不同的关键字做出反应。我怀疑你是否可以在 VBA 中原生地做到这一点,但是一个用更通用的语言编写的对象是否可以检测它是否被传递
ByVal
和 return a .Clone
本身。
- 换句话说,VBA 的内置数据类型如何传递
ByVal
以及任何其他对象是否可以模仿此行为(同样,在 Python 中,一切都是对象,因此一切行为都可以复制*)。还是我不必担心这种情况会发生?
- 是否可以模仿
Array
的行为并在传递时引发编译器错误 ByVal
- 因为数组没有克隆方法并且无法轻易复制,所以总是 ByRef
- 是否有 Sub、Function 或方法可以让我创建对象的深度副本?也就是说,复制一个对象使用的内存并创建对象的第二个副本的一种通用方法。
*轶事,我对Python
很陌生
In all the tutorials and references I've been through to date, ByRef was always "passing the object", ByVal was "passing a copy of the object".
你有link上面的例子吗?
对于对象(从 classes 创建)ByVal
类似于 *foo
而 ByRef
类似于 **foo
。
Boolean 和 Long 是原语,因为原语 ByVal
类似于 bar
而 ByRef
类似于 *bar
.
- 没有
- 对象无法判断它是否已通过
ByRef
或 ByVal
。
- VBA 'built-in' 数据类型在 C/C++ 中有类似物 所以
Long
是一个 32 位整数 所以 Boolean
实际上也是,但只能取两个值之一(0=False,-1=True)。您不必担心,VBA 以安全的名义设置了限制。
- 如果您想为您的对象强制传递
ByRef
,请创建一个类型而不是 class。
对于 classes,您将不得不编写自己的浅层和深层复制构造函数。但是类型可以通过浅层和深层使用 =
来复制。
数组和类型被传递 ByRef
因为它们是在堆栈上创建的。
对于 VBA 中的对象和 类,传递 byval 或 byref 没有真正的区别。通过byref ony意味着传递指针'byref',对象保持不变
如果你想到它,它对 (Excel) 个对象有意义;你是否通过
Range("A1") byref 或 byval,它仍然是我们正在寻址的同一个单元格。
我想我最近(终于)开始了解 ByVal
和 ByRef
在 VBA 中的工作方式。
观察
在我迄今为止阅读的所有教程和参考资料中,ByRef
始终是 "passing the object",ByVal
始终是 "passing a copy of the object"。对我来说,后者意味着对象在内存中的位置被复制,指向新位置的指针被 returned。我现在意识到情况并非总是如此,事实上,据我所知,这种情况很少发生:相反,大多数对象和 classes 实际上都通过了 ByRef
,即使指定了 ByVal
在例程的签名中。
A 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
A VB Array
在传递 ByVal 时引发编译器错误,可能只是针对此
问题
所有让我想到的:
- 对于 classes/objects ,
- 区分可以传递
ByVal
的某些类型(Boolean
、Long
)与 class 和不能传递的类型 - 在图书馆参考方面 -
VB6
中可能没有写的东西- 是否可以创建具有默认
ByVal
响应的对象?- 在 Python 中,您可以指定 class 如何对不同的关键字做出反应。我怀疑你是否可以在 VBA 中原生地做到这一点,但是一个用更通用的语言编写的对象是否可以检测它是否被传递
ByVal
和 return a.Clone
本身。 - 换句话说,VBA 的内置数据类型如何传递
ByVal
以及任何其他对象是否可以模仿此行为(同样,在 Python 中,一切都是对象,因此一切行为都可以复制*)。还是我不必担心这种情况会发生?
- 在 Python 中,您可以指定 class 如何对不同的关键字做出反应。我怀疑你是否可以在 VBA 中原生地做到这一点,但是一个用更通用的语言编写的对象是否可以检测它是否被传递
- 是否可以模仿
Array
的行为并在传递时引发编译器错误ByVal
- 因为数组没有克隆方法并且无法轻易复制,所以总是ByRef
- 是否可以创建具有默认
- 是否有 Sub、Function 或方法可以让我创建对象的深度副本?也就是说,复制一个对象使用的内存并创建对象的第二个副本的一种通用方法。
ByVal
和 ByRef
之间有什么区别(如果有)
*轶事,我对Python
很陌生In all the tutorials and references I've been through to date, ByRef was always "passing the object", ByVal was "passing a copy of the object".
你有link上面的例子吗?
对于对象(从 classes 创建)
ByVal
类似于*foo
而ByRef
类似于**foo
。Boolean 和 Long 是原语,因为原语
ByVal
类似于bar
而ByRef
类似于*bar
.- 没有
- 对象无法判断它是否已通过
ByRef
或ByVal
。 - VBA 'built-in' 数据类型在 C/C++ 中有类似物 所以
Long
是一个 32 位整数 所以Boolean
实际上也是,但只能取两个值之一(0=False,-1=True)。您不必担心,VBA 以安全的名义设置了限制。
- 对象无法判断它是否已通过
- 如果您想为您的对象强制传递
ByRef
,请创建一个类型而不是 class。
- 没有
对于 classes,您将不得不编写自己的浅层和深层复制构造函数。但是类型可以通过浅层和深层使用
=
来复制。
数组和类型被传递 ByRef
因为它们是在堆栈上创建的。
对于 VBA 中的对象和 类,传递 byval 或 byref 没有真正的区别。通过byref ony意味着传递指针'byref',对象保持不变
如果你想到它,它对 (Excel) 个对象有意义;你是否通过 Range("A1") byref 或 byval,它仍然是我们正在寻址的同一个单元格。