将数组分配给属性let时的行为不一致

时间:2016-10-05 11:23:55

标签: arrays vba excel-vba properties excel

请考虑以下代码:

Option Explicit

Public Property Let Dummy(v() As Integer)

End Property

Public Sub LetDummy(v() As Integer)

End Sub

Sub Foo()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Bar i ' internal error 51
End Sub

Sub Foo2()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Bar2 i ' no error
End Sub

Sub Foo3()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Dummy = i ' no error
End Sub

Sub Bar(j() As Integer)
    Dummy = j
End Sub

Sub Bar2(j() As Integer)
    LetDummy j
End Sub

当我运行宏观' Foo'我收到消息'内部错误51'但是' Foo2'和' Foo3'运行正常。这种行为的原因是什么?或者它只是VBA中的一个错误?如何解决这个错误?

背景:在我的应用程序中,我想将作为函数参数提供的数组赋值给array类型的属性。

3 个答案:

答案 0 :(得分:4)

对我来说这看起来像个错误,因为错误51说如果你得到它就联系MS。

如果我创建一个局部变量,分配它,然后传递它似乎工作。

非常hacky。

Option Explicit

Private ary() As Integer

Public Property Get Dummy() As Integer()
    Dummy = ary
End Property

Public Property Let Dummy(v() As Integer)
    Debug.Print "In Dummy" & UBound(v)
    ary = v

End Property

Public Sub LetDummy(v() As Integer)
    Debug.Print "In LetDummy" & UBound(v)
End Sub

Sub Foo()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Call Bar(i)  ' internal error 51
End Sub

Sub Foo2()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Bar2 i ' no error
End Sub

Sub Foo3()
    ReDim i(0 To 2) As Integer
    i(0) = 3
    i(1) = 45
    i(2) = 10

    Dummy = i ' no error
End Sub

Sub Bar(j() As Integer)
    Dim i() As Integer
    i = j

    Dummy = i

    Dim x As Integer
    Dim myary() As Integer
    myary = Dummy
    For x = 0 To 2
        Debug.Print myary(x)
    Next
End Sub

Sub Bar2(j() As Integer)
    LetDummy j
End Sub

答案 1 :(得分:1)

使用 $html=""; $pqr = mysqli_query($link,$sql); while($row = mysqli_fetch_row($pqr)) { $html .= "<a class='fancybox' rel='gallery1' href='".$row[2].$row[3]."' width='270'>"; $html .= "<img src='".$row[2].$row[3]."' width='270'>"; $html .= "</a>"; } echo $html; } echo $html;

Variant

答案 2 :(得分:1)

这是一个非常有趣的错误。在尝试重现它时,我提出了这个:

Public Property Let Foo(ByRef values() As Long)
End Property

Public Sub Test()
    Dim values(0 To 1) As Long
    values(0) = 1
    values(1) = 2
    IllegalByRefArray values
End Sub

Private Sub IllegalByRefArray(ByRef values() As Long)
    Stop 'break here or crash Excel
    Foo = values
End Sub

而不是运行时错误51,我遇到了严重的崩溃。 “Excel已经停止工作了”,而且噗不通。运行64位Excel 2010。

在附加了Visual Studio调试器的情况下运行Excel,我可以看到EXCEL.EXE进程如何彻底破坏Foo = values任务:

  

EXCEL.EXE中0x00007FFF834E4B90(ntdll.dll)处的未处理异常:0xC0000028:在展开操作期间遇到无效或未对齐的堆栈

所以肯定会发生一些可疑的事情。事实上,暴露数组的属性首先是一种奇怪的东西:通常你将封装数组并公开索引属性,即这样的事情:

Private internal() As Long 'todo initialize

Public Property Let Foo(ByVal index As Long, ByVal value As Long)
    internal(index) = value
End Property

Public Property Get Foo(ByVal index As Long) As Long
    Foo = internal(index)
End Property

尽管如此,该语言并没有明确禁止将数组暴露为属性......或者它是什么?

这是一个编译时错误,即 无法分配给数组

Public Property Let Foo(ByRef values() As Long)
End Property

Public Sub Test()
    Dim values(0 To 1) As Long
    values(0) = 1
    values(1) = 2
    Foo = values ' compile error / can't assign to array
End Sub

所以将数组传递给一个程序然后将它传递给Property Let似乎是一个利用VBE相当差的静态代码分析的黑客,以便绕过编译错误,这可能是有原因的。< / p>

我们知道我们无法在VBA中传递数组ByVal。尝试这样做甚至不是编译错误,它是语法错误,VBE显然对它不满意:

Private Sub IllegalByValArray(ByVal values() As Long)

这指向隐式传递参数ByVal的属性的Rory's comment。这是一些证据:

Public Property Let Foo(ByRef values As Variant)
    Debug.Print "Foo: " & VarPtr(values)
End Property

Public Sub Test()
    Dim values(0 To 1) As Long
    values(0) = 1
    values(1) = 2

    Dim v As Variant
    v = values

    Debug.Print "Test: " & VarPtr(v)
    IllegalByRefArray v
End Sub

Private Sub IllegalByRefArray(ByRef values As Variant)
    Debug.Print "IllegalByRefArray: " & VarPtr(values)
    Foo = values
End Sub

运行Test过程会打印此(或类似)输出:

Test: 196542488
IllegalByRefArray: 196542488
Foo: 196542352

请注意,变量指针在TestIllegalByRefArray中是相同的,但Foo中的变量指针不同,而不管ByRef修饰符。如果您将IllegalByRefArray中的修饰符更改为ByVal,则会获得3个不同的值:

Test: 196542488
IllegalByRefArray: 196542384
Foo: 196542336

从接受的答案:

  

如果我创建一个局部变量,分配它,然后传递它似乎工作。

当然可以;你现在传递的指针不同于你收到的指针,VBA通过上面描述的几种方式尝试,以阻止你将指向数组的指针传递给Property Let mutator:VBA试图告诉你很多方面,你所做的不是你应该做的事情。

以正确的方式执行操作,保持数组封装,并公开索引属性,而不是允许数组本身被覆盖。

如果你不关心封装,那么把它变成一个公共领域:该属性实际上没有用处。

Public Values() As Long

在那里,随意分配。

如果必须为该数组提供Property Let mutator,那么干净的解决方案是pass the array as a Variant;是的,这意味着您需要调整代码的其余部分。

TL; DR:

ByRef mutator 上的Property Let修饰符毫无意义:这是公然谎言(它是通过引用传递!)并使您的代码非常混乱。不要在ByRef值参数上粘贴Property Let修饰符只是为了满足语言语法,否则会拒绝传递数组参数 - 您正在扭曲VBA的手臂,危及用户未保存的工作,以及让你的代码说谎。

改为传递Variant,或通过索引属性正确封装数组。其他任何东西只是一个肮脏的恶臭黑客。