【问题标题】:Write result of function to a variable, where result could be an object将函数的结果写入变量,其中结果可以是对象
【发布时间】:2018-06-28 10:30:48
【问题描述】:

如果我有一个可能返回对象或原始类型的函数 - 在其中我可以执行以下操作来处理这两种情况:

Function Result() As Variant 'may be object or not
    '... get item - the return value
    If IsObject(item) Then
        Set Result = item
    Else
        Result = item
    End If
End Function

但是,如何在不运行两次函数的情况下对存储Result 的变量进行相同的测试?例如

Dim myResult As Variant
If IsObject(Result) Then 'test return type of function
    Set myResult = Result
Else
    myResult = Result
End If

作为

myResult = Result 'fails if Result returns object
Set myResult = Result 'fails if Result returns non-object

我正在尝试将一系列对象/非对象写入变体类型的数组

【问题讨论】:

  • 我很好奇您不知道返回什么的细节。理论上,您已经声明了一个 var 来捕获返回,所以您什么时候不知道返回了什么?
  • @Jeeped 我正在编写一个缓冲区类,它需要一个数组/内容集合。它循环遍历将其添加到内部商店的内容。一旦内部存储达到一定容量,就会引发一个事件,并将存储中的内容写入一个可以读取的数组。由于内部存储不断被写入,我无法公开它,所以我需要以某种方式克隆它,这需要将未知类型的项目写入变量数组。

标签: vba excel object variant


【解决方案1】:

一种可能的解决方案是通过传递ByRef 直接写入变量。在标准模块中:

Property Let LetSet(ByRef variable As Variant, ByVal value As Variant)
    If IsObject(value) Then
        Set variable = value
    Else
        variable = value
    End If
End Sub

像这样称呼

Dim myResult As Variant
LetSet(myResult) = 123                'myResult = 123
LetSet(myResult) = New Collection     'Set myResult = New Collection

【讨论】:

【解决方案2】:

作为一个类的 sn-p 示例,它处理动态调用类函数。如果可以基于RubberDuck Reflection API 实现一些基本的 VBA 反射,那么它的原型设计工作正在进行中。由于我正在动态调用类和模块函数,因此没有可靠的方法可以提前知道函数调用是否会返回原始类型或对象类型。

注意我还没有测试代码,需要在 InvokeMember 函数中添加一些错误处理。

Greedo 的解决方案类似,期望函数结果是一个单独的引用参数,这样可以放入函数调用,例如。 CallByName 作为第二个参数。第一个参数是存储引用调用结果的变量。函数结果参数 y 是通过引用作为数组可能产生的,并避免在传入时将整个数组复制到内存中。

Public Function InvokeMember(ByVal obj As Object, ParamArray args() As Variant) As Variant
    Assign InvokeMember, CallByName(obj, Me.FullName, Me.CallType, args)
End Function

在一个模块中,来自VBA Extension Library的代码

' Assign x to y regardless of object or primitive
Public Sub Assign(ByRef x as Variant, ByRef y as Variant)

    If IsObject(y) Then
        Set x = y
    Else
        x = y
    End If

End Sub

感谢这个问题,它帮助我解决了相同的问题,避免在处理返回的对象、数组或原始数据类型时调用相同的函数两次。

【讨论】:

  • 除非我遗漏了重要的一点,否则这似乎并不能解决 OP 的问题。外部调用者仍然必须调用 InvokeMember() 并将其结果存储在某处,这就是出现相同问题的地方 - 您需要在未来查看是否应该使用 x = InvokeMember()Set x = InvokeMember()
  • 我明白了; CallByNameOutput 很重要,应该设为 public,而 InvokeMember 不相关,应该从答案中删除。然后 OP 将其称为Dim myResult As Variant : CallByNameOutput Result(), myResult
  • 抱歉刚刚更新以简化命名。我想可以做 Assign DeclarationTypeClassFunction.InvokeMember(obj, args), varResult。刚刚意识到 Assign sub 在一个试图理解的项目中是多么的方便。
  • 现在您不希望 y 成为 byval 以避免创建数组副本。
  • 是的,公共子系统更有意义,因为它会被广泛使用。啊,这就是为什么他们在整个 RubberDuck VBEX 项目中都有分配。几个星期以来,答案一直摆在我面前。 :)
【解决方案3】:

另一种解决方案是使用 Array() 函数并将函数或属性输出包装在其中。
当我测试我的原始解决方案时,从内存中它不适用于值来自类的属性的地方。这是使用接口或直接使用属性并获得变体结果时的问题。
注意:我认为它不适用于返回 UDT 或固定字符串长度的函数/属性。

对于您的示例,请使用以下内容:

Dim myResult As Variant
Dim resultOutput as Variant
resultOutput = Array(Result)
If VBA.IsObject(resultOutput(0)) Then
    set myResult = resultOutput(0)
Else
    myResult = resultOutput(0)
End If

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    相关资源
    最近更新 更多