如何在VBA中为Variant分配Variant?

时间:2016-03-02 14:46:06

标签: vba types variant

(警告:虽然乍看之下可能看起来像是一个,但是一个初学者级别的问题。如果你熟悉“强迫”或者你曾经有过这样的话查看了VBA规范,请继续阅读。)

假设我有一个Variant类型的表达式,我想将它分配给一个变量。听起来很简单吧?

Dim v As Variant

v = SomeMethod()    ' SomeMethod has return type Variant

不幸的是,如果SomeMethod返回一个Object(即VarType为vbObject的Variant),Let coercion将启动并且v包含该对象的“简单数据值”。换句话说,如果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的解决方案?

5 个答案:

答案 0 :(得分:11)

在VBA中,将Variant分配给您不知道它是对象还是基元的变量的唯一方法是将其作为参数传递。

如果你不能重构你的代码,以便v作为参数传递给Sub,Function或Let属性(尽管Let这也适用于对象),你总是可以声明{ {1}}在模块范围内,并且只有一个专用Sub用于保存 - 分配该变量:

v

与其他人调用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()
  

[...]我猜VBA中没有LetSet v = ...语句

现在有: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

根据类型,您没有LetSet v, SomeMethod()Let变量所需的返回值,而是将应该保存返回值的变量作为第一个参数通过引用,以便Sub可以更改其值。

答案 1 :(得分:3)

Dim v As Variant
For Each v In Array(SomeMethod())
    Exit For 'Needed for v to retain it's value
Next v
'Use v here - v is now holding a value or a reference

答案 2 :(得分:2)

您可以使用错误捕获来减少预期的方法调用次数。首先尝试设置。如果成功 - 没问题。否则,只需指定:

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个调用 - 在这种情况下,您可能应该重构代码。

答案 3 :(得分:1)

It appears that I wasn't the only one with this issue.

解决方案已交给我here

简而言之:

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

这似乎类似于答案中描述的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 函数,但我认为这还是很有用的。

答案 4 :(得分:1)

Dim v As Variant
Dim a As Variant
a = Array(SomeMethod())
If IsObject(a(0)) Then
    Set v = a(0)
Else
    v = a(0)
End If