有没有办法来防止在VBAdynamic数组中的额外元素?

正如标题所述,有没有办法阻止额外的元素显示在VBAdynamic数组中,当他们是非零基础?

例如,使用类似于以下的代码时:

While Cells(ndx, 1).Value <> vbNullString ReDim Preserve data(1 To (UBound(data) + 1)) ndx = ndx + 1 Wend 

处理结束时,您有一个额外的空数组元素。 虽然这可以通过以下方式消除:

 ReDim Preserve data(1 To (UBound(data) - 1)) 

这似乎不是解决这个问题的最好方法。

因此,是否有办法防止多余的元素被创build呢? 最好是在循环内部不需要额外的逻辑。

ReDim Preserve是一个相对昂贵的操作,可能在每次迭代中都不需要做任何事情。 最好是将数组重新设置为比所需要的更大的上限(可能是块),然后在完成时将数组缩小到所需的上限。

此外,你可能想要调查其他读取Excel范围到数组的方式,例如

  Dim a() With Sheet1 a = .Range(.Range("A1"), .Range("A1").End(xlDown)).Value End With Debug.Print a(1, 1) 

循环往往很慢:)

Visual Basic数组是从零开始的。 不过,这可以使用Option Base语句进行更改。

用你的数组,额外的元素是因为你做了一个UBound() + 1 ,UBound会给你正确的数字已经。 如果数组有5个元素,UBound将是5.但最后一个索引将是4,所以ReDim到UBound会给你一个数组大小+1。

由于数组无论如何都是痛苦的(在VBA中),而ReDim Preserve是一个对新的固定大小的数组进行数组复制的操作,所以我build议您在任何地方都可以使用Collections。 它们更容易迭代( For Each ... In ... ),并且在添加,查找和删除元素方面效率更高。

 ' creation ' Dim anyValue as Variant Dim c as New Collection ' and adding values ' c.Add anyValue, strKey ' iteration ' For Each anyValue in c Debug.Print anyValue Next c ' count values ' Debug.Print c.Count ' element deletion ' c.Delete strKey 

(您可以使用VBScript中的Scripting.Dictionary来增加舒适度,但是您需要先引用它。)

您也可以通过简单地将它们放在彼此的内部来具有多个维度的集合。

因此,这似乎是一个令人讨厌的小问题,因为看起来真的没有办法阻止这个问题出现。 根据其他用户提供的答案,我厌倦了以下解决问题的方法。

使用集合 – 尽pipe这种方法在需要读取和存储数据的情况下非常有用,但不能将用户定义的types与集合一起使用。 能够定义项目键是有用的,因为您可以使用它来交叉引用两个集合; 然而,在VBA中, 没有办法获得Collection中可以限制的密钥列表 。

将一个Excel范围读入一个数组 – 另一个非常好的方法,但是当你知道范围将提前时,它似乎工作得最好。 如果您必须快速确定范围,那么您可能会发现自己处于使用Collection或更小的ReDim数组更易于使用的情况。

使用ReDim Preserve实时构buildarrays – 虽然这可能是一个相当简单的操作,但涉及到两个问题。 一个是, ReDim可能是一个昂贵的操作,因为Visual Basic实际上创build一个具有给定大小的新数组,复制旧数组,然后删除旧数组(或在Visual Basic .NET中释放垃圾收集器)。 所以,如果你打算使用非常大的数组,那么你会想最小化对ReDim的调用。

另外,你很可能会遇到类似于在数组起始处有一个额外元素或数组末尾是空的问题。 唯一的解决办法是在执行操作之前检查是否需要resize,或者在返回结果之前删除空元素。

VB6和COM使用基于0和1的索引的混合(数组是基于0的,除非当您使用Option Base进行更改或以其他方式明确声明时)。

在早期的COM对象模型中,COM集合通常是基于1的,但有时基于0的…

Dim / Redim数据(1到N)是从1到N索引的N个元素的数组

Dim / Redim数据(0到N-1)是从0到N-1索引的N个元素的数组

Dim / Redim数据(N)是从0到N索引的N + 1个元素的数组(如果Option Base为0)

数据(N)通常是指数据(0到N),它是N + 1个元素的数组。

就个人而言,我总是将数组显式声明为(0到N-1),并且不依赖于Option Base,对于使用多种语言的开发人员更为熟悉。

有一个边界情况:VBA不支持零长度数组,您必须始终至less有一个元素(对于multidimensional array中的每个维度)。 因此,可以声明的最小数组是一个元素的数据(0到0)或数据(1到1)。

在你的情况下,我怀疑你正在创build一个元素的数组,然后每次通过循环添加一个元素:

 ReDim data(1 To 1) While Cells(ndx, 1).Value <> vbNullString ReDim Preserve data(1 To (UBound(data) + 1)) ndx = ndx + 1 Wend 

相反(并且暂时不考虑在循环中调用ReDim Preserve的效率),您应该使用:

 ReDim data(1 To 1) nStartIndex = ndx While Cells(ndx, 1).Value <> vbNullString ' On the first iteration this does nothing because ' the array already has one element ReDim Preserve data(1 To ndx - nStartIndex + 1) ndx = ndx + 1 Wend 

这个问题已经有一段时间了, 然而,我今天遇到了同样的问题,并解决了这个问题:

有办法做到这一点,通过定义一个永远不会被使用的第一个元素。 如果你需要一个基于零的数组,你可以像这样定义一个空数组

 Dim data() Dim i as Long ReDim data(-1 To -1) ' Empty array. We never use data(-1). For i = 0 To UBound(data) ... Next i 

如果你需要一个基于1的数组,你可以这样做

 ReDim data(0 To 0) ' Empty array. We never use data(0). For i = 1 To UBound(data) ... Next i 

诚然,自从我完成了经典的VB6以来,这已经有一段时间了,而且我的VBA经验甚至更加生疏……如果memeory服务的话,VB的数组语法不仅仅是基数是1而不是0。说,你指定的“大小”并不表示数组中元素的总数,而是最后一个索引是可寻址的。