我目前正在为 VBA 文件创建一个类对象,其目标是充当可以传递单个单元格的范围字典。如果此单元格包含在某个范围内,则它返回与相应范围键关联的值。类名称是“rangeDic”。
它正在制作中,因此其功能尚未实现。这是代码:
Private zone() As String
Private bounds() As String
Private link As Dictionary
Const ContextId = 33
'Init zone
Private Sub Class_Initialize()
Set link = New Dictionary
ReDim zone(0)
ReDim bounds(0)
End Sub
'properties
Property Get linkDico() As Dictionary
Set linkDico = link
End Property
Property Set linkDico(d As Dictionary)
Set link = d
End Property
Property Get pZone() As String()
pZone = zone
End Property
Property Let pZone(a() As String)
Let zone = a
End Property
'methods
Public Sub findBounds()
Dim elmt As String
Dim i As Integer
Dim temp() As String
i = 1
For Each elmt In zone
ReDim Preserve bounds(i)
temp = Split(elmt, ":")
bounds(i - 1) = temp(0)
bounds(i) = temp(1)
i = i + 2
Next elmt
End Sub
Run Code Online (Sandbox Code Playgroud)
我试图在测试子中实例化它,以便调试中间概念。这是代码:
Sub test()
Dim rd As rangeDic
Dim ran() As String
Dim tabs() As Variant
Dim i As Integer
i = 1
With ThisWorkbook.Worksheets("DataRanges")
While .Cells(i, 1).Value <> none
ReDim Preserve ran(i - 1)
ReDim Preserve tabs(i - 1)
ran(i - 1) = .Cells(i, 1).Value
tabs(i - 1) = .Cells(i, 3).Value
i = i + 1
Wend
End With
Set rd = createRangeDic(ran, tabs)
End Sub
Public Function createRangeDic(zones() As String, vals() As Variant) As rangeDic
Dim obje As Object
Dim zonesL As Integer
Dim valsL As Integer
Dim i As Integer
zonesL = UBound(zones) - LBound(zones)
valsL = UBound(vals) - LBound(vals)
If zonesL <> valsL Then
Err.Raise vbObjectError + 5, "", "The key and value arrays are not the same length.", "", ContextId
End If
Set obje = New rangeDic
obje.pZone = zones()
For i = 0 To 5
obje.linkDico.add zones(i), vals(i)
Next i
Set createRangeDic = obje
End Function
Run Code Online (Sandbox Code Playgroud)
看一下 的第 2 行Public Function createRangeDic。我必须将我的对象声明为“Object”,如果我尝试将其声明为“rangeDic”,Excel 将在第 行崩溃obje.pZone = zones()。查看 Windows 事件日志后,我可以看到“错误 1000”类型的应用程序未知错误导致崩溃,其中“VB7.DLL”是有问题的包。
为什么这样 ?难道我做错了什么 ?
感谢您的帮助
编辑:我在 Excel 2016 下工作
看起来这是一个错误。我的 Excel 没有崩溃,但出现“内部错误”。
\n因为您有 Java 背景,所以首先让我们澄清一些事情。
\n数组只能通过引用传递
\n在 VBA 中,数组只能通过引用另一个方法来传递(除非将其包装在 Variant 中)。所以,这个声明:
\nProperty Let pZone(a() As String) \'Implicit declaration\nRun Code Online (Sandbox Code Playgroud)\n等价于:
\nProperty Let pZone(ByRef a() As String) \'Explicit declaration\nRun Code Online (Sandbox Code Playgroud)\n当然,还有这个:
\nPublic Function createRangeDic(zones() As String, vals() As Variant) As rangeDic\nRun Code Online (Sandbox Code Playgroud)\n等价于:
\nPublic Function createRangeDic(ByRef zones() As String, ByRef vals() As Variant) As rangeDic\nRun Code Online (Sandbox Code Playgroud)\n如果您尝试像这样声明方法参数:ByVal a() As String您只会收到编译错误。
数组在赋值时被复制
\n假设有两个数组称为a和b,在执行诸如将数组a = b的副本b分配给之类的操作时a。让我们测试一下。在标准模块中删除以下代码:
Option Explicit\n\nSub ArrCopy()\n Dim a() As String\n Dim b() As String\n \n ReDim b(0 To 0)\n b(0) = 1\n \n a = b\n a(0) = 2\n \n Debug.Print "a(0) = " & a(0)\n Debug.Print "b(0) = " & b(0)\nEnd Sub\nRun Code Online (Sandbox Code Playgroud)\n\n如图所示,更改 array 时, array 的内容b不受影响a。
ByVal无论您是否指定,属性 Let 始终接收其参数ByRef
让我们测试一下。创建一个名为的类Class1并添加以下代码:
Option Explicit\n\nPublic Property Let SArray(ByRef arr() As String)\n arr(0) = 1\nEnd Property\n\nPublic Function SArray2(ByRef arr() As String)\n arr(0) = 2\nEnd Function\nRun Code Online (Sandbox Code Playgroud)\n现在创建一个标准模块并添加以下代码:
\nOption Explicit\n\nSub Test()\n Dim c As New Class1\n Dim arr() As String: ReDim arr(0 To 0)\n \n arr(0) = 0\n Debug.Print arr(0) & " - value before passing to Let Property"\n c.SArray = arr\n Debug.Print arr(0) & " - value after passing to Let Property"\n \n arr(0) = 1\n Debug.Print arr(0) & " - value before passing to Function"\n c.SArray2 arr\n Debug.Print arr(0) & " - value after passing to Function"\nEnd Sub\nRun Code Online (Sandbox Code Playgroud)\n\n因此,这个简单的测试证明了Property Let即使只能传递数组,也会复制数组ByRef。
错误
\n您的原始ran变量 ( )以新名称Sub test传递ByRef,然后再次传递到(属性)。在正常情况下,根据需要多次传递数组应该没有问题,但在这里似乎这是一个问题,因为正在尝试制作副本。createRangeDiczonesByRefpZoneLetByRefProperty Let
有趣的是,如果我们替换它(在里面createRangeDic):
obje.pZone = zones()\nRun Code Online (Sandbox Code Playgroud)\n有了这个:
\nDim x() As String\nx = zones\nobje.pZone = x\nRun Code Online (Sandbox Code Playgroud)\nobje即使声明了,代码运行也没有问题As rangeDic。这是有效的,因为该x数组是该数组的副本zones。
看起来无法复制已传递多次Property Let的数组,但如果仅传递一次,它就可以正常工作。也许是因为调用堆栈中添加堆栈帧的方式,存在内存访问问题,但很难说。不管问题是什么,这似乎都是一个错误。ByRefByRef
与问题无关,但我必须补充一些内容:
\nReDim Preserve是一个坏主意,因为每次为新(更大)数组分配新内存,并且每个元素都会从旧数组复制到新数组。这非常慢。相反,使用Collection注释中建议的 as\n@DanielDu\xc5\xa1ek 或最小化调用次数ReDim Preserve(例如,如果您知道将有多少个值,则只需在开始时对数组进行一次尺寸标注)。Range非常慢。Range使用Range.Value或Range.Value2属性(我更喜欢后者)将整个内容读入数组。只要范围超过 1 个单元格,这两种方法都会返回一个数组。linkDico公开了可以从主类实例外部修改的内部字典。也许它不会破坏您的特定示例中的任何内容,但值得一提。另一方面Property Get pZone() As String()是安全的,因为这会返回内部数组的副本。Option Explicit到所有模块/类的顶部,以确保强制执行正确的变量声明。你的代码无法为我编译,因为none不存在于 VBA 中,除非你在项目的其他地方有它。打开该选项后,我发现了一些其他问题。