How can I assign a Variant to a Variant in VBA?

后端 未结 5 1719
不知归路
不知归路 2020-12-05 19:45

(Warning: Although it might look like one at first glance, this is not a beginner-level question. If you are familiar with the phrase \"Let coercion\" o

相关标签:
5条回答
  • 2020-12-05 19:58

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

    The solution was given to me here.

    In short:

    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
    

    It seems this is similar to the LetSet() function described in the answer, but I figured this'd be useful anyway.

    0 讨论(0)
  • 2020-12-05 19:59
    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
    
    0 讨论(0)
  • 2020-12-05 20:04

    You could use error trapping to reduce the expected number of method calls. First try to set. If that succeeds -- no problem. Otherwise, just assign:

    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
    

    When I ran the code, the first time I got 1.55, which is less than the 2.00 you would get if you repeated the experiment but with the error-handling approach replaced by the naïve if-then-else approach you discussed in your question.

    Note that the more often the function returns an object, the less function calls on average. If it almost always returns an object (e.g. that is what it is supposed to return but returns a string describing an error condition in certain cases) then this way of doing things will approach 1 call per setting/ assigning the variable. On the other hand -- if it almost always returns a primitive value then you will approach 2 calls per assignment -- in which case perhaps you should refactor your code.

    0 讨论(0)
  • 2020-12-05 20:07

    In VBA, the only way to assign a Variant to a variable where you don't know if it is an object or a primitive, is by passing it as a parameter.

    If you cannot refactor your code so that the v is passed as a parameter to a Sub, Function or Let Property (despite the Let this also works on objects), you could always declare v in module scope and have a dedicated Sub solely for the purpose of save-assigning that variable:

    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
    

    with somewhere else calling SetV SomeMethod().

    Not pretty, but it's the only way without calling SomeMethod() twice or touching its inner workings.


    Edit

    Ok, I mulled over this and I think I found a better solution that comes closer to what you had in mind:

    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
    

    [...] I guess there just is no LetSet v = ... statement in VBA

    Now there is: LetSet v, SomeMethod()

    You don't have a return value that you need to Let or Set to a variable depending of its type, instead you pass the variable that should hold the return value as first parameter by reference so that the Sub can change its value.

    0 讨论(0)
  • 2020-12-05 20:14
    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
    
    0 讨论(0)
提交回复
热议问题