vba 数组作为类成员

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25328975/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-12 04:15:44  来源:igfitidea点击:

Array as a Class Member

arraysvbaclass-members

提问by Blackhawk

I'm designing a dynamic buffer for outgoing messages. The data structure takes the form of a queue of nodes that have a Byte Array buffer as a member. Unfortunately in VBA, Arrays cannot be public members of a class.

我正在为传出消息设计一个动态缓冲区。数据结构采用节点队列的形式,这些节点具有字节数组缓冲区作为成员。不幸的是,在 VBA 中,数组不能是类的公共成员。

For example, this is a no-no and will not compile:

例如,这是一个禁忌并且不会编译:

'clsTest

Public Buffer() As Byte

You will get the following error: "Constants, fixed-length strings, arrays, user-defined types and Declare statements not allowed as Public members of object modules"

您将收到以下错误:“常量、固定长度字符串、数组、用户定义类型和声明语句不允许作为对象模块的公共成员”

Well, that's fine, I'll just make it a private member with public Property accessors...

好吧,那很好,我只是让它成为具有公共属性访问器的私有成员......

'clsTest

Private m_Buffer() As Byte

Public Property Let Buffer(buf() As Byte)
    m_Buffer = buf
End Property

Public Property Get Buffer() As Byte()
    Buffer = m_Buffer
End Property

...and then a few tests in a module to make sure it works:

...然后在模块中进行一些测试以确保其正常工作:

'mdlMain

Public Sub Main()
    Dim buf() As Byte
    ReDim buf(0 To 4)

    buf(0) = 1
    buf(1) = 2
    buf(2) = 3
    buf(3) = 4


    Dim oBuffer As clsTest
    Set oBuffer = New clsTest

    'Test #1, the assignment
    oBuffer.Buffer = buf    'Success!

    'Test #2, get the value of an index in the array
'    Debug.Print oBuffer.Buffer(2)   'Fail
    Debug.Print oBuffer.Buffer()(2)    'Success!  This is from GSerg's comment

    'Test #3, change the value of an index in the array and verify that it is actually modified
    oBuffer.Buffer()(2) = 27
    Debug.Print oBuffer.Buffer()(2)  'Fail, diplays "3" in the immediate window
End Sub

Test #1 works fine, but Test #2 breaks, Bufferis highlighted, and the error message is "Wrong number of arguments or invalid property assignment"

测试 #1 工作正常, 但测试 #2 中断,Buffer突出显示,错误消息是“参数数量错误或属性分配无效”

Test #2 now works! GSerg points out that in order to call the Property Get Buffer()correctly and also refer to a specific index in the buffer, TWOsets of parenthesis are necessary: oBuffer.Buffer()(2)

测试 #2 现在有效!GSerg指出,为了调用Property Get Buffer()正确,也可以指特定的索引缓冲区中, 两个组的括号是必要的:oBuffer.Buffer()(2)

Test #3 fails - the original value of 3 is printed to the Immediate window. GSerg pointed out in his comment that the Public Property Get Buffer()only returns a copy and not the actual class member array, so modifications are lost.

测试 #3 失败 - 原始值 3 被打印到立即窗口。GSerg 在他的评论中指出,Public Property Get Buffer()只返回一个副本而不是实际的类成员数组,因此修改丢失。

How can this third issue be resolved make the class member array work as expected?

如何解决第三个问题,使类成员数组按预期工作?

(I should clarify that the general question is "VBA doesn't allow arrays to be public members of classes. How can I get around this to have an array member of a class that behaves as if it was for all practical purposes including: #1 assigning the array, #2 getting values from the array, #3 assigning values in the array and #4 using the array directly in a call to CopyMemory(#3 and #4 are nearly equivalent)?)"

(我应该澄清一下,一般问题是“VBA 不允许数组是类的公共成员。我怎样才能解决这个问题,让类的数组成员表现得好像是为了所有实际目的,包括:# 1 分配数组,#2 从数组中获取值,#3 在数组中分配值,#4 在调用中直接使用数组CopyMemory(#3 和 #4 几乎等效)?)”

采纳答案by Blackhawk

So it turns out I needed a little help from OleAut32.dll, specifically the 'VariantCopy'function. This function faithfully makes an exact copy of one Variant to another, including when it is ByRef!

所以事实证明我需要 OleAut32.dll 的帮助,特别是“VariantCopy”函数。此函数忠实地将一个 Variant 精确复制到另一个 Variant,包括当它是 ByRef 时!

'clsTest

Private Declare Sub VariantCopy Lib "OleAut32" (pvarDest As Any, pvargSrc As Any)

Private m_Buffer() As Byte

Public Property Let Buffer(buf As Variant)
    m_Buffer = buf
End Property

Public Property Get Buffer() As Variant
    Buffer = GetByRefVariant(m_Buffer)
End Property

Private Function GetByRefVariant(ByRef var As Variant) As Variant
    VariantCopy GetByRefVariant, var
End Function

With this new definition, all the tests pass!

有了这个新定义,所有的测试都通过了!

'mdlMain

Public Sub Main()
    Dim buf() As Byte
    ReDim buf(0 To 4)

    buf(0) = 1
    buf(1) = 2
    buf(2) = 3
    buf(3) = 4


    Dim oBuffer As clsTest
    Set oBuffer = New clsTest

    'Test #1, the assignment
    oBuffer.Buffer = buf    'Success!

    'Test #2, get the value of an index in the array
    Debug.Print oBuffer.Buffer()(2)    'Success!  This is from GSerg's comment on the question

    'Test #3, change the value of an index in the array and verify that it is actually modified
    oBuffer.Buffer()(2) = 27
    Debug.Print oBuffer.Buffer()(2)  'Success! Diplays "27" in the immediate window
End Sub

回答by mtholen

@Blackhawk,

@黑鹰,

I know it is an old post, but thought I'd post it anyway.

我知道这是一个旧帖子,但我想我还是会发布它。

Below is a code I used to add an array of points to a class, I used a subclass to define the individual points, it sounds your challenge is similar:

下面是我用来向类添加点数组的代码,我使用了一个子类来定义各个点,听起来您的挑战是类似的:

Mainclass tCurve

主类tCurve

Private pMaxAmplitude As Double
Private pCurvePoints() As cCurvePoint

Public cDay As Date
Public MaxGrad As Double

Public GradChange As New intCollection
Public TideMax As New intCollection
Public TideMin As New intCollection
Public TideAmplitude As New intCollection
Public TideLow As New intCollection
Public TideHigh As New intCollection

Private Sub Class_Initialize()

    ReDim pCurvePoints(1 To 1500)
    ReDim curvePoints(1 To 1500) As cCurvePoint

    Dim i As Integer

    For i = 1 To 1500
        Set Me.curvePoint(i) = New cCurvePoint
    Next

End Sub

Public Property Get curvePoint(Index As Integer) As cCurvePoint

    Set curvePoint = pCurvePoints(Index)

End Property

Public Property Set curvePoint(Index As Integer, Value As cCurvePoint)

    Set pCurvePoints(Index) = Value

End Property

subclass cCurvePoint

子类 cCurvePoint

Option Explicit

Private pSlope As Double
Private pCurvature As Double
Private pY As Variant
Private pdY As Double
Private pRadius As Double
Private pArcLen As Double
Private pChordLen As Double

Public Property Let Slope(Value As Double)
    pSlope = Value
End Property

Public Property Get Slope() As Double
    Slope = pSlope
End Property

Public Property Let Curvature(Value As Double)
    pCurvature = Value
End Property

Public Property Get Curvature() As Double
    Curvature = pCurvature
End Property

Public Property Let valY(Value As Double)
    pY = Value
End Property

Public Property Get valY() As Double
    valY = pY
End Property

Public Property Let Radius(Value As Double)
    pRadius = Value
End Property

Public Property Get Radius() As Double
    Radius = pRadius
End Property

Public Property Let ArcLen(Value As Double)
    pArcLen = Value
End Property

Public Property Get ArcLen() As Double
    ArcLen = pArcLen
End Property

Public Property Let ChordLen(Value As Double)
    pChordLen = Value
End Property

Public Property Get ChordLen() As Double
    ChordLen = pChordLen
End Property

Public Property Let dY(Value As Double)
    pdY = Value
End Property

Public Property Get dY() As Double
    dY = pdY
End Property

This will create a tCurve with 1500 tCurve.Curvepoints().dY (for example)

这将创建一个 tCurve 与 1500 tCurve.Curvepoints().dY(例如)

The trick is to get the index process correct in the main class !

诀窍是让主类中的索引过程正确!

Good luck !

祝你好运 !

回答by Dustin

Not the most elegant solution, but modeling from the code you provided...

不是最优雅的解决方案,而是根据您提供的代码进行建模...

In clsTest:

在 clsTest 中:

Option Explicit

Dim ArrayStore() As Byte

Public Sub AssignArray(vInput As Variant, Optional lItemNum As Long = -1)
    If Not lItemNum = -1 Then
        ArrayStore(lItemNum) = vInput
    Else
        ArrayStore() = vInput
    End If
End Sub

Public Function GetArrayValue(lItemNum As Long) As Byte
    GetArrayValue = ArrayStore(lItemNum)
End Function

Public Function GetWholeArray() As Byte()
    ReDim GetWholeArray(LBound(ArrayStore) To UBound(ArrayStore))
    GetWholeArray = ArrayStore
End Function

And in mdlMain:

在 mdlMain 中:

Sub test()
Dim buf() As Byte
Dim bufnew() As Byte
Dim oBuffer As New clsTest

    ReDim buf(0 To 4)
    buf(0) = 1
    buf(1) = 2
    buf(2) = 3
    buf(3) = 4

    oBuffer.AssignArray vInput:=buf
    Debug.Print oBuffer.GetArrayValue(lItemNum:=2)

    oBuffer.AssignArray vInput:=27, lItemNum:=2
    Debug.Print oBuffer.GetArrayValue(lItemNum:=2)

    bufnew() = oBuffer.GetWholeArray
    Debug.Print bufnew(0)
    Debug.Print bufnew(1)
    Debug.Print bufnew(2)
    Debug.Print bufnew(3)

End Sub

I added code to pass the class array to another array to prove accessibility.

我添加了代码以将类数组传递给另一个数组以证明可访问性。

Even though VBA won't allow us to pass arrays as properties, we can still use Functions to pick up where properties fall short.

即使 VBA 不允许我们将数组作为属性传递,我们仍然可以使用函数来找出属性不足的地方。