(警告:虽然乍看之下可能看起来像是一个问题,但这不是初学者级别的问题。如果你熟悉“强迫”一词,或者你曾经研究过VBA规范,请继续阅读。)
假设我有Variant
类型的表达式,我想将它分配给变量。听起来很简单吧?
Dim v As Variant
v = SomeMethod() ' SomeMethod has return type Variant
不幸的是,如果SomeMethod
返回一个Object(即VarType为vbObject的Variant),则Let coercion将启动并且v
包含该对象的“Simple data value”。换句话说,如果SomeMethod返回对TextBox的引用,则v
将包含一个字符串。
显然,解决方案是使用Set
:
Dim v As Variant
Set v = SomeMethod()
遗憾的是,如果SomeMethod
没有返回一个对象,例如,一个字符串,产生类型不匹配错误。
到目前为止,我找到的唯一解决方案是:
Dim v As Variant
If IsObject(SomeMethod()) Then
Set v = SomeMethod()
Else
v = SomeMethod()
End If
这有两次叫SomeMethod
的不幸副作用。
有没有一个解决方案,不需要两次调用SomeMethod
?
在VBA中,将Variant分配给您不知道它是对象还是基元的变量的唯一方法是将其作为参数传递。
如果你不能重构你的代码,以便将v
作为参数传递给Sub,Function或Let属性(尽管Let
也适用于对象),你总是可以在模块范围内声明v
并且只有一个专用的Sub用于保存的目的 - 分配该变量:
Private v As Variant
Private Sub SetV(ByVal var As Variant)
If IsObject(var) Then
Set v = var
Else
v = var
End If
End Sub
在别的地方打电话给SetV SomeMethod()
。
不漂亮,但这是没有两次调用SomeMethod()
或触及其内部工作的唯一方法。
好吧,我仔细研究了这个,我想我找到了一个更接近你想到的更好的解决方案:
Public Sub LetSet(ByRef variable As Variant, ByVal value As Variant)
If IsObject(value) Then
Set variable = value
Else
variable = value
End If
End Sub
[...]我猜VBA中没有LetSet v = ...语句
现在有:LetSet v, SomeMethod()
根据类型,您没有Let
或Set
所需的返回值,而是通过引用传递应该将返回值作为第一个参数保存的变量,以便Sub可以更改其值。
您可以使用错误捕获来减少预期的方法调用次数。首先尝试设置。如果成功 - 没问题。否则,只需指定:
Public counter As Long
Function Ambiguous(b As Boolean) As Variant
counter = counter + 1
If b Then
Set Ambiguous = ActiveSheet
Else
Ambiguous = 1
End If
End Function
Sub test()
Dim v As Variant
Dim i As Long, b As Boolean
Randomize
counter = 0
For i = 1 To 100
b = Rnd() < 0.5
On Error Resume Next
Set v = Ambiguous(b)
If Err.Number > 0 Then
Err.Clear
v = Ambiguous(b)
End If
On Error GoTo 0
Next i
Debug.Print counter / 100
End Sub
当我运行代码时,我第一次得到1.55,如果你重复实验,但是错误处理方法被你在问题中讨论的天真的if-then-else
方法所取代,那么你将得到的小于2.00。
请注意,函数返回对象的频率越高,平均调用的函数就越少。如果它几乎总是返回一个对象(例如它应该返回它但在某些情况下返回一个描述错误条件的字符串),那么这种处理方式将接近每个设置1个调用/分配变量。另一方面 - 如果它几乎总是返回一个原始值,那么每个赋值将接近2个调用 - 在这种情况下,您可能应该重构代码。
It appears that I wasn't the only one with this issue.
解决方案给了我here。
简而言之:
Public Declare Sub VariantCopy Lib "oleaut32.dll" (ByRef pvargDest As Variant, ByRef pvargSrc As Variant)
Sub Main()
Dim v as Variant
VariantCopy v, SomeMethod()
end sub
这似乎与答案中描述的LetSet()
函数类似,但我认为这无论如何都是有用的。