CallByName如果参数是Variant数组设置为单元格范围,则会出现意外结果

时间:2014-12-22 20:45:22

标签: excel vba

我一直在特定应用程序中使用CallByName并获得无法解释的结果。它们在具有以下条件的简单测试中是可重复的

  • 类对象的属性是Type Double
  • 添加的值(Let)来自已设置为多个单元格区域的变量数组

我很感激这种行为的解释。以下代码应该重现它(至少在Excel 2007 / Windows 7中)

工作表单元格 A1 包含 5.8

A2 包含 1.3 ,而A列中的其余单元格为空白。

课程模块(class1)

Private pMyData

Public Property Get MyData()
    MyData = pMyData
End Property
Public Property Let MyData(Value)
    pMyData = Value
End Property

常规模块

Option Explicit
Sub foo()
    Dim class1 As class1

Dim V(1 To 2, 1 To 1) As Variant
V(1, 1) = [a1]
V(2, 1) = [a2]

    Set class1 = New class1
    CallByName class1, "MyData", VbLet, V(1, 1)

    Debug.Print V(1, 1), class1.MyData  ' <-- 5.8           5.8

Dim W As Variant
W = Range("A1:A2")

    Set class1 = New class1
    CallByName class1, "MyData", VbLet, W(1, 1)

    Debug.Print W(1, 1), class1.MyData  ' <-- 5.8           312080296

    CallByName class1, "MyData", VbLet, CDbl(W(1, 1))

    Debug.Print W(1, 1), class1.MyData  ' <-- 5.8           5.8

End Sub

注意第二个debug.print行显示存储在class1.MyData中的值是312080296而不是5.8。

2 个答案:

答案 0 :(得分:3)

这里也是一样的。获取145842640.如果它有帮助您不必使用CallByName。使用以下行可以帮我将其正确设置为5.8。

class1.MyData = W(1, 1)

也可能有助于将pMyData声明为double,也可以在Let / Get语句中声明。然后你会在尝试分配时遇到错误,比如第一个V(1,1),它会强制你明确地声明转换,这在这种情况下似乎是一件好事(或者说是必要的)。

无法找到一个很好的快速理由,为什么它会这样做,或转换实际上在做什么。希望有人知道,我现在很好奇。

编辑 - 看起来CallByName实际上是将W(1,1)的地址传递给Let语句。 (换句话说,传递指针的值。)它似乎通过CDbl解引用转换指针,获取值,这就是它与显式转换一起工作的原因。 (或者我认为无论如何。)

尝试添加此功能:

Public Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias _
    "VarPtr" (Var() As Any) As LongPtr

然后为W(1,1)执行debug.pring,为VarPtr执行debug.print(W(1,1))。我发现W(1,1)的myData和VarPtr值是同一个。我认为这是CallByName函数行为的一部分,只要传递地址,而不是值,但我没有时间进一步研究。希望有所帮助。

答案 1 :(得分:0)

我让这条线正常工作,

CallByName class1, "MyData", VbLet, CVar(W(1, 1))

我唯一能想到的是CallByName期望Args()VariantW(1,1)无法隐式投放..(出于某种原因)